0 view
Tutorial Mon premier thème : « Aube »
Unread message

Niveau débutant · environ 45 minutes · NeoFrag Reborn 1.2.50 ou plus récent

Un thème décide de l'allure de tout le site : l'en-tête, le pied de page, les couleurs, la police, et où tombent les widgets. Les modules et les widgets, eux, ne choisissent jamais une couleur : ils prennent celles du thème. Dans ce tutoriel, tu écris « Aube », un thème clair et chaleureux, et tu l'actives sur ton site.

L'accueil avec le thème Aube

Tout ce que tu vois au centre — le cadre des actualités, l'espace membre, le formulaire de connexion — vient des modules et des widgets du site. Le thème n'écrit que l'en-tête, le pied de page et la palette.

L'archive terminée, aube-1.0.0.zip, est jointe à ce sujet.

Ce qu'il te faut

  • un site NeoFrag Reborn sur lequel tu es administrateur, de préférence un site d'essai : activer un thème change ce que voient tous les visiteurs ;
  • un éditeur de texte qui enregistre en UTF-8 ;
  • un peu de CSS aide : on n'écrit que des choses simples (des couleurs, des marges, une rangée de liens).

Étape 1 — Le dossier

aube/
├── aube.php                 la carte d'identité, les zones, les fichiers chargés, la mise en page par défaut
├── views/
│   ├── body.tpl.php         le squelette de chaque page
│   └── live_editor/
│       ├── row.tpl.php      les styles de ligne proposés dans l'éditeur en direct
│       └── widget.tpl.php   les styles de widget
├── css/
│   └── style.css            la palette et l'allure
├── langs/                   les traductions
└── images/
    └── thumbnail.jpg        sa vignette : une photo de l'accueil, 960 × 600

Étape 2 — La carte d'identité

aube/aube.php

<?php
declare(strict_types=1);
/**
 * Aube — le thème du tutoriel « Mon premier thème » (niveau débutant). LGPLv3.
 */

namespace NF\Themes\Aube;

use NF\NeoFrag\Addons\Theme;

class Aube extends Theme
{
	protected function __info()
	{
		return [
			'title'       => 'Aube',
			'description' => $this->lang('Un thème clair et chaleureux : un en-tête avec le menu, le contenu au centre, un pied de page sobre.'),
			'link'        => 'https://neofrag-reborn.xyz',
			'author'      => 'NeoFrag Reborn',
			'license'     => 'LGPLv3 <https://www.gnu.org/licenses/lgpl-3.0.html>',
			'version'     => '1.0.0',
			'depends'     => [
				'neofrag' => '1.2.50'
			],
			'core'        => FALSE,
			'presets'     => [],
			'requires'    => [],
			'zones'       => ['Navigation', 'Avant-contenu', 'Contenu', 'Pied de page'],
			'regions'     => [
				'navigation'     => 'Navigation',
				'before_content' => 'Avant-contenu',
				'content'        => 'Contenu',
				'footer'         => 'Pied de page'
			]
		];
	}

	public function __init()
	{
		$this	->css('bootstrap.min')
				->css('nf-bs5-bridge')
				->css('icons/fontawesome.min')
				->css('nf-socle-themes')
				->css('style')
				->css('nf-apres-theme')
				->js('bootstrap.bundle.min')
				->js('modal')
				->js('notify')
				->js('confirm');
	}

	public function styles_row()
	{
		return $this->view('live_editor/row');
	}

	public function styles_widget()
	{
		return $this->view('live_editor/widget');
	}

	public function install($dispositions = [])
	{
		$dispositions = $this->array();

		// Toutes les pages : le menu dans l'en-tête… Ses titres s'écrivent en français : c'est le widget Navigation
		// qui les traduit, à l'affichage, dans la langue du visiteur.
		$dispositions->set('*', 'Navigation', $this->array([
			$this->row(
				$this->col(
					$this->widget($this->db->insert('nf_widgets', [
						'widget'   => 'navigation',
						'type'     => 'index',
						'settings' => json_encode([
							'links' => [
								['title' => utf8_htmlentities('Accueil'),    'url' => ''],
								['title' => utf8_htmlentities('Actualités'), 'url' => 'news'],
								['title' => utf8_htmlentities('Forum'),      'url' => 'forum'],
								['title' => utf8_htmlentities('Membres'),    'url' => 'members'],
								['title' => utf8_htmlentities('Contact'),    'url' => 'contact']
							]
						])
					]))
				)
			)
		]));

		// … et le contenu de la page, avec l'espace membre à côté.
		$dispositions->set('*', 'Contenu', $this->array([
			$this->row(
				$this->col(
					$this->widget($this->db->insert('nf_widgets', [
						'widget' => 'module',
						'type'   => 'index'
					]))
				)->size('col-12 col-lg-8'),
				$this->col(
					$this->widget($this->db->insert('nf_widgets', [
						'widget' => 'user',
						'type'   => 'index'
					]))->style('panel-header')
				)->size('col-12 col-lg-4')
			)->style('row-default')
		]));

		return parent::install($dispositions);
	}
}

Ce fichier est le plus long du tutoriel ; il se lit par morceaux.

Les zones et les régions. Une zone est un emplacement où l'éditeur en direct pose des widgets. Aube en a quatre : la barre du menu, une bande avant le contenu, le contenu, le pied de page. zones les nomme dans l'ordre — c'est ce nom que l'administrateur voit dans l'éditeur. regions donne à chacune un nom de code, que le squelette de l'étape 3 emploie. Ne change plus l'ordre des zones une fois le thème installé : le site retient chaque zone par son rang.

__init() : ce que chaque page charge, dans cet ordre :

Fichier Ce qu'il apporte
bootstrap.min, bootstrap.bundle.min Bootstrap 5, la base des boutons, des grilles et des fenêtres
nf-bs5-bridge Bootstrap aux couleurs de ton thème
icons/fontawesome.min les icônes
nf-socle-themes le socle commun des thèmes : cadres, formulaires, listes, styles des widgets — dessinés avec ta palette
style ta feuille, css/style.css (étape 4)
nf-apres-theme rétablit ce qu'une feuille de thème écrase sans le vouloir ; toujours après la tienne
modal, notify, confirm les fenêtres, les notifications et les confirmations du site

Le socle commun fait le gros du travail : grâce à lui, un thème peut se contenter de sa palette et de son squelette.

styles_row() et styles_widget() rendent les styles que l'éditeur en direct propose pour une ligne et pour un widget (étape 6).

install(), la mise en page par défaut. À la première activation, le site appelle install() : elle pose les widgets de départ, pour que le site ne soit pas vide. Aube pose le menu (le widget navigation) dans la zone « Navigation » de toutes les pages ('*'), puis, dans « Contenu », une ligne de deux colonnes : le contenu de la page (le widget module) et l'espace membre (le widget user).

  • ->size('col-12 col-lg-8') : toute la largeur sur un téléphone, deux tiers sur un grand écran. Écris toujours les deux : un col-8 seul écraserait la page sur un téléphone.
  • ->style('panel-header') : l'espace membre avec un titre coloré.
  • Les titres du menu s'écrivent en français : le widget Navigation les traduit à l'affichage, dans la langue de chaque visiteur. Les liens vers un module absent du site (le forum, par exemple) ne s'affichent simplement pas.

Étape 3 — Le squelette

views/body.tpl.php est le HTML de chaque page :

aube/views/body.tpl.php

<header class="au-tete">
	<div class="container au-tete-in">
		<a class="au-nom" href="<?php echo url('') ?>"><?php echo nf_texte($this->config->nf_name) ?></a>

		<?php if ($menu = $this->output->region('navigation')): ?>
			<nav class="au-menu" aria-label="<?php echo $this->lang('Navigation') ?>"><?php echo $menu ?></nav>
		<?php endif ?>

		<div class="au-compte">
			<?php if ($this->user()): ?>
				<a class="btn btn-outline-secondary btn-sm" href="<?php echo url('user') ?>"><i class="fas fa-user"></i> <?php echo nf_texte($this->user->username) ?></a>
				<?php if ($this->access->effective_admin()): ?>
					<a class="btn btn-primary btn-sm" href="<?php echo url('admin') ?>"><?php echo $this->lang('Administration') ?></a>
				<?php endif ?>
			<?php else: ?>
				<a class="btn btn-primary btn-sm" href="<?php echo url('user/login') ?>"><?php echo $this->lang('Connexion') ?></a>
			<?php endif ?>
		</div>
	</div>
</header>

<main class="container au-page">
	<?php if ($zone = $this->output->region('before_content')): ?>
		<section class="au-zone"><?php echo $zone ?></section>
	<?php endif ?>

	<?php if ($zone = $this->output->region('content')): ?>
		<section class="au-zone"><?php echo $zone ?></section>
	<?php endif ?>
</main>

<footer class="au-pied">
	<div class="container">
		<?php if ($zone = $this->output->region('footer')): ?>
			<div class="au-zone"><?php echo $zone ?></div>
		<?php endif ?>

		<p class="au-pied-ligne">
			<?php echo nf_texte($this->config->nf_name) ?>
			· <?php echo $this->lang('Propulsé par') ?> <a href="https://neofrag-reborn.xyz">NeoFrag Reborn</a>
			· <?php echo nf_liens_legaux() ?>
		</p>

		<div class="au-pied-outils">
			<?php echo nf_selecteur_theme() ?>
			<?php echo nf_selecteur_langue() ?>
		</div>
	</div>
</footer>
  • $this->output->region('content') rend les widgets d'une zone, par son nom de code. Le if ($zone = …) évite de dessiner un cadre vide quand la zone n'a aucun widget.

  • $this->config->nf_name est le nom du site ; $this->user() dit si le visiteur est connecté, et $this->access->effective_admin() s'il est administrateur.

  • url('user/login'), url('admin') : des adresses du site, construites avec url().

  • Trois aides du site, à écrire dans tout pied de page :

    • nf_liens_legaux() — les mentions légales, la confidentialité, et « Gérer mes cookies », que chaque visiteur doit pouvoir retrouver ;
    • nf_selecteur_theme() — le choix du thème, s'il y en a plusieurs ;
    • nf_selecteur_langue() — le choix de la langue, si le site en a plusieurs.

    Chacune ne montre rien quand elle n'a rien à proposer.

Étape 4 — La palette

aube/css/style.css

/* La palette d'Aube : toutes les couleurs que les modules et les widgets emploient. */
:root {
	--nf-bg: #f6f3ee;
	--nf-bg-elevated: #ffffff;
	--nf-surface: #ffffff;
	--nf-surface-2: #f1ece5;
	--nf-surface-3: #e8e0d5;
	--nf-hover: #efe8df;
	--nf-border: #e2d9cd;
	--nf-border-strong: #c9bba8;
	--nf-text: #2a2622;
	--nf-text-strong: #15120f;
	--nf-text-soft: #4d463f;
	--nf-text-muted: #675e55;
	--nf-muted: #675e55;
	--nf-muted-2: #756b61;
	--nf-accent: #c2410c;
	--nf-accent-soft: rgba(194, 65, 12, .1);
	--nf-accent-strong: #9a3412;
	--nf-accent-text: #9a3412;
	--nf-on-accent: #ffffff;
	--nf-success: #15803d;
	--nf-info: #0369a1;
	--nf-warning: #b45309;
	--nf-danger: #b91c1c;
	--nf-danger-soft: rgba(185, 28, 28, .1);
	--nf-radius: 12px;
	--nf-radius-sm: 8px;

	/* Celles que le socle commun des thèmes demande en plus. */
	--nf-radius-xs: 3px;
	--nf-shadow-sm: 0 1px 2px rgba(42, 38, 34, .08);
	--nf-shadow: 0 6px 18px -8px rgba(42, 38, 34, .2);
	--nf-shadow-md: 0 12px 30px -12px rgba(42, 38, 34, .28);
	--nf-success-soft: rgba(21, 128, 61, .1);
	--nf-warning-soft: rgba(180, 83, 9, .1);
	--nf-info-soft: rgba(3, 105, 161, .1);
	--nf-link: var(--nf-accent-text);
	--nf-link-hover: var(--nf-accent);
	--nf-font-mono: ui-monospace, "Cascadia Mono", Consolas, monospace;

	/* Les polices : celle du texte, et celle des titres. */
	--nf-font: system-ui, "Segoe UI", Roboto, sans-serif;
	--nf-font-display: "Iowan Old Style", Georgia, serif;
}

body {
	background: var(--nf-bg);
	color: var(--nf-text);
	font-family: var(--nf-font);
}

/* L'en-tête : le nom du site, le menu, le compte. */
.au-tete {
	background: var(--nf-bg-elevated);
	border-bottom: 1px solid var(--nf-border);
	box-shadow: var(--nf-shadow-sm);
}

.au-tete-in {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 12px 24px;
	padding-block: 14px;
}

.au-nom {
	font-family: var(--nf-font-display);
	font-size: 1.4rem;
	font-weight: 700;
	color: var(--nf-text-strong);
	text-decoration: none;
}

.au-nom::before {
	content: "";
	display: inline-block;
	width: .7em;
	height: .7em;
	margin-right: .45em;
	border-radius: 50%;
	background: var(--nf-accent);
}

.au-menu {
	flex: 1 1 auto;
	min-width: 0;
}

/* Le menu est un widget : on efface la grille qui l'entoure, et on aligne ses liens. */
.au-menu .row {
	--bs-gutter-x: 0;
	margin: 0;
}

.au-menu .row > * > .widget {
	margin: 0;
}

.au-menu .nav {
	gap: 4px;
	flex-wrap: wrap;
}

.au-menu .nav-link {
	padding: 6px 12px;
	border-radius: var(--nf-radius-sm);
	color: var(--nf-text-soft);
	font-weight: 600;
}

.au-menu .nav-link:hover,
.au-menu .nav-link.active {
	color: var(--nf-accent-strong);
	background: var(--nf-accent-soft);
}

.au-compte {
	display: flex;
	gap: 8px;
}

/* Le contenu. */
.au-page {
	padding-block: 28px;
}

.au-zone + .au-zone {
	margin-top: 24px;
}

/* Le pied de page. */
.au-pied {
	border-top: 1px solid var(--nf-border);
	padding-block: 24px;
	color: var(--nf-text-muted);
	font-size: .9rem;
}

.au-pied-outils {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

La première moitié est la palette : les couleurs --nf-… que tous les modules et widgets du site emploient. Un thème doit les définir toutes. Celle qui manquerait retomberait sur une valeur de secours — un bloc blanc au milieu d'un thème sombre, par exemple.

Famille Les variables
fonds --nf-bg (la page), --nf-bg-elevated et --nf-surface… (ce qui est posé dessus), --nf-hover
traits --nf-border, --nf-border-strong
textes --nf-text, --nf-text-strong, --nf-text-soft, --nf-text-muted, --nf-muted…
accent --nf-accent (la couleur du thème), ses variantes, et --nf-on-accent, la couleur du texte posé sur l'accent
états --nf-success, --nf-info, --nf-warning, --nf-danger
formes et polices --nf-radius…, --nf-shadow…, --nf-font, --nf-font-display, --nf-font-mono

Choisis des couleurs lisibles : un texte doit trancher sur son fond (un rapport de contraste de 4,5 pour 1 au moins). L'orange d'Aube, #c2410c, passe à 4,7 sur le fond de la page, et le blanc posé dessus à 5,2.

La seconde moitié habille l'en-tête et le pied de page. Le menu est un widget : il arrive entouré de la grille de la page (.row, colonnes) ; les règles .au-menu .row… effacent cette grille pour aligner les liens sur une ligne.

Étape 5 — Les traductions

langs/ porte les textes du thème — « Connexion », « Propulsé par », les noms des styles — dans les six langues, comme pour un widget. Copie le dossier de l'archive.

Étape 6 — Les styles de l'éditeur en direct

Quand l'administrateur pose une ligne ou un widget, l'éditeur en direct lui propose des styles. Aube en propose deux pour une ligne, views/live_editor/row.tpl.php :

aube/views/live_editor/row.tpl.php

<div class="nf-le-style-section">
	<h6 class="nf-le-style-grid-title"><?php echo icon('fas fa-square-full') ?> <?php echo $this->lang('Apparence de la ligne') ?></h6>
	<div class="nf-le-style-grid" data-nf-target="row">
		<button type="button" class="nf-le-style-card live-editor-overview" data-style="row-default" data-label="<?php echo $this->lang('Fond transparent') ?>">
			<span class="nf-le-style-label"><?php echo $this->lang('Fond transparent') ?></span>
		</button>
		<button type="button" class="nf-le-style-card live-editor-overview" data-style="row-dark" data-label="<?php echo $this->lang('Fond sombre') ?>">
			<span class="nf-le-style-label"><?php echo $this->lang('Fond sombre') ?></span>
		</button>
	</div>
</div>

et trois pour un widget, views/live_editor/widget.tpl.php :

aube/views/live_editor/widget.tpl.php

<div class="nf-le-style-section">
	<h6 class="nf-le-style-grid-title"><?php echo icon('far fa-square') ?> <?php echo $this->lang('Apparence du widget') ?></h6>
	<div class="nf-le-style-grid" data-nf-target="widget">
		<button type="button" class="nf-le-style-card live-editor-overview" data-style="panel-default" data-label="<?php echo $this->lang('Widget classique') ?>">
			<span class="nf-le-style-label"><?php echo $this->lang('Widget classique') ?></span>
		</button>
		<button type="button" class="nf-le-style-card live-editor-overview" data-style="panel-header" data-label="<?php echo $this->lang('Titre coloré') ?>">
			<span class="nf-le-style-label"><?php echo $this->lang('Titre coloré') ?></span>
		</button>
		<button type="button" class="nf-le-style-card live-editor-overview" data-style="panel-color" data-label="<?php echo $this->lang('Widget coloré') ?>">
			<span class="nf-le-style-label"><?php echo $this->lang('Widget coloré') ?></span>
		</button>
	</div>
</div>

data-style est la classe posée sur la ligne ou le panneau : row-dark, panel-header, panel-color… Le socle commun les dessine avec ta palette, tu n'as rien à écrire pour elles.

Installer et activer

  1. Compresse le dossier aube, puis Thèmes & addons → Ajouter. Le site répond « Le thème Aube a été installé ».
  2. Sur la carte d'Aube, ouvre le menu (les trois points) et choisis Activer. Le site répond « Aube activé ». À cette première activation, le site pose la mise en page de install().
  3. Ouvre ton site.

Tu veux revenir en arrière ? Active de nouveau ton ancien thème : sa mise en page n'a pas bougé. Et si tu modifies install() après coup, Réinstaller par défaut, dans le même menu, repose la mise en page d'origine — en effaçant celle que tu as arrangée dans l'éditeur en direct.

La vignette

La carte d'un thème, dans « Thèmes & addons », montre images/thumbnail.jpg : une photo de l'accueil, au format 960 × 600, en JPEG. Fais une capture de ton site avec le thème actif, recadre-la à ce format, enregistre-la sous ce nom. L'archive en contient une.

Si ça ne marche pas

Ce que tu vois Ce que ça veut dire
« Le composant ne peut pas être installé, veuillez vérifier la présence des numéros de version » Il manque 'version' ou 'depends' dans aube.php.
« Le composant ne peut pas être installé, veuillez vérifier son contenu » L'archive ne contient pas aube/aube.php, ou la ligne use NF\NeoFrag\Addons\Theme; a changé.
Le site est tout blanc, sans couleurs css/style.css ne se charge pas : vérifie ->css('style') dans __init(), et le nom du dossier css.
Un widget posé dans une zone n'apparaît pas Le squelette ne rend pas cette zone : chaque nom de regions doit avoir son region('…') dans body.tpl.php.
Tu as changé install(), et la mise en page reste l'ancienne install() ne joue qu'à la première activation : Réinstaller par défaut repose la nouvelle.
Une page blanche, ou « Une erreur est survenue » Une faute de frappe dans un fichier PHP : compare-le à celui de l'archive.

Pour aller plus loin

  • Change l'accent : remplace #c2410c partout où il apparaît, et vérifie le contraste avec un outil en ligne (« WCAG contrast checker »).
  • Ajoute une zone « Avant le pied de page » : un nom dans zones, un nom de code dans regions, un region() dans le squelette.
  • Le guide Créer un thème, dans la documentation du site, décrit tout ce qu'un thème peut faire.

Le tutoriel intermédiaire reprend Aube : des couleurs que l'administrateur choisit lui-même, avec un aperçu en direct, un mode sombre, et une mise en page d'accueil.


Forum activity
There is 0 user on the forum
no visitor
Statistics
  • 72 topics created
  • 0 message posted
  • 63 announcements
  • 1 participant

Manage my cookies

The site only places the cookies it needs to work: they do not require your consent. The other services belong to other companies; they only load if you accept them. Your choice is kept for six months, and you can change it at any time.

Needed by the site Always active

  • __Host-session Keeps you logged in and protects forms against fraudulent submissions. Until the browser is closed; one year with “Remember me”
  • nf_consent Keeps the choices you make in this window. 6 months
  • nf_fuseau Shows dates and times in your time zone. 1 year
  • Browser storage Keeps your choice between day and night mode, and the layout chosen for the list of articles. Until you clear it

Videos

Videos embedded in pages: the player comes from the platform, which receives your IP address and may place its cookies.

Music

Tracks and playlists embedded in pages: the player comes from the platform, which receives your IP address and may place its cookies.