Últimas publicaciones
Tutorial Mon premier thème : « Aube »
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.

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 : uncol-8seul é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. Leif ($zone = …)évite de dessiner un cadre vide quand la zone n'a aucun widget. -
$this->config->nf_nameest 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 avecurl(). -
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
- Compresse le dossier
aube, puis Thèmes & addons → Ajouter. Le site répond « Le thème Aube a été installé ». - 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(). - 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
#c2410cpartout 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 dansregions, unregion()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.

Actividad del foro
ningún visitante
Estadísticas
- 72 temas creados
- 0 mensaje publicado
- 63 anuncios
- 1 participante