0 vue
Tutoriel Mon premier thème : « Aube »
Message non lu

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.


Activité du forum
Il y a 3 utilisateurs sur le forum
3 visiteurs
Statistiques
  • 72 sujets créés
  • 0 message posté
  • 63 annonces
  • 1 participant

Gérer mes cookies

Le site ne dépose que les cookies nécessaires à son fonctionnement : ils ne demandent pas votre accord. Les autres services appartiennent à d’autres sociétés ; ils ne se chargent que si vous les acceptez. Votre choix est gardé six mois, et vous pouvez le changer à tout moment.

Nécessaires au site Toujours actifs

  • __Host-session Garde votre connexion et protège les formulaires contre les envois frauduleux. Jusqu’à la fermeture du navigateur ; un an avec « Se souvenir de moi »
  • nf_consent Garde les choix que vous faites dans cette fenêtre. 6 mois
  • nf_fuseau Affiche les dates et les heures dans votre fuseau horaire. 1 an
  • Stockage du navigateur Garde votre choix entre le mode jour et le mode nuit, et la présentation choisie pour la liste des articles. Jusqu’à ce que vous le vidiez

Vidéos

Les vidéos intégrées aux pages : le lecteur vient de la plateforme, qui reçoit votre adresse IP et peut déposer ses cookies.

Musique

Les morceaux et listes de lecture intégrés aux pages : le lecteur vient de la plateforme, qui reçoit votre adresse IP et peut déposer ses cookies.