4 Parcours ExpertLeçon 3 sur 8 8 min de lecture

Personnaliser les gabarits du thème

  1. 1. L'architecture d'Éclair
  2. 2. Développement local et tests
  3. 3. Personnaliser les gabarits du thème
  4. 4. Nginx, VPS, proxy et CDN : déployer Éclair
  5. 5. Performance d'Éclair : garder 100 sur 100
  6. 6. Migrer depuis WordPress ou Wix
  7. 7. Sécurité avancée et maintenance
  8. 8. Industrialiser Éclair en agence
Sommaire
  1. CSS personnalisé ou gabarits du thème ?
  2. Les fichiers d'app/theme
  3. Comment un gabarit reçoit ses données ?
  4. Les contraintes de la CSP
  5. Performance des gabarits
  6. Mises à jour, intégrité et manifest.php
  7. L'exemple de cette académie
  8. À retenir
  9. Pour aller plus loin
  10. Questions fréquentes

Les gabarits du thème décident du HTML de chaque page publique : en-tête, menus, article, listes, fiche produit. Les modifier ouvre tout ce que le CSS personnalisé ne permet pas, au prix d'une maintenance à chaque mise à jour. Cette leçon détaille les fichiers d'app/theme, les variables reçues, les contraintes de sécurité et l'exemple de cette académie.

Ce que tu sauras faire

Choisir le bon niveau

CSS personnalisé, feuille du thème ou gabarit PHP.

Modifier un gabarit

Avec les variables de $v et les fonctions d'Éclair.

Rester compatible

CSP stricte, mises à jour et contrôle d'intégrité.

CSS personnalisé ou gabarits du thème ?

Commence toujours par la solution la plus légère. Le CSS personnalisé couvre la grande majorité des retouches, et le <body> porte des classes qui permettent de cibler un contexte sans toucher au PHP : theme-<nom>, hdr-center, cards-list, puis is-post, is-page, is-wide, is-list, is-shop, is-product, is-search ou is-404 selon la page.

CritèreCSS personnaliséGabarits du thème
OùRéglages › Apparence › CSS personnaliséfichiers d'app/theme/
Rangé dansdata/custom-css.phpapp/, remplacé à chaque mise à jour
Après une mise à jourconservéà réappliquer
Contrôle d'intégritérien à signalerfichiers « modifiés » ou « inconnus »
Limites64 Ko, sans < ni @importHTML libre, dans le respect de la CSP
Idéal pourcouleurs, espacements, style d'un blocstructure du HTML, nouveaux éléments

Les fichiers d'app/theme

FichierRôleVariables principales de $v
layout.phpsquelette commun : <head>, en-tête, menus, pied de pagelang, head, css, content, current, id, class
single.phppage ou articleitem, html, toc, author, category, crumbs, related, translations, featured, words, h1_in_content
list.phpblog, catégorie, auteur, recherche, cataloguetitle, description, entries, crumbs, page, pages, prev, next, query, empty, chips
cards.php, products.phpgrilles de cartes d'articles et de produitsentries, lang, level
product.phpfiche produititem, entry, html, images, offers, crumbs, related
404.phppage introuvablelang
theme.cssstyles de base, insérés dans chaque pagerepères /*dark*/ et /*shop*/
themes/*.cssles 5 thèmes optionnelsajoutés après theme.css
directions.cssmorceaux des directions artistiquesseuls ceux de la direction choisie

Les variables CSS de theme.css (--bg, --bg-soft, --card, --text, --muted, --line, --radius, --shadow, --text-w, --wide-w) et celles calculées depuis les réglages (--accent, --on-accent, --link, --font, --font-head) restent ta meilleure prise : utilise-les dans tes gabarits pour suivre le thème, le mode sombre et la couleur choisie.

Comment un gabarit reçoit ses données ?

Theme::render($vue, $v) exécute d'abord le gabarit de la vue (single, list…), place son HTML dans $v['content'], puis exécute layout.php avec le même tableau. Le squelette voit donc toutes les variables de la page : $v['item'] ou $v['translations'] sont disponibles dans l'en-tête d'un article. Pour une page en français, Theme::typography() ajoute enfin les espaces insécables.

Theme::partial($nom, $v) inclut app/theme/<nom>.php dans une fonction isolée : seul $v y est visible. Tu peux ainsi découper ton thème en petits fichiers (Theme::partial('lang-switch', $v)), en sachant que chaque nouveau fichier apparaîtra parmi les « fichiers inconnus » du contrôle d'intégrité.

OutilUsage dans un gabarit
e($valeur)échapper tout texte ou attribut, sans exception
path_to('/chemin')adresse interne, compatible avec un sous-dossier
asset('site/site.js')adresse d'un fichier statique avec son empreinte de version
Theme::t('clé', $lang)textes du thème en six langues (read_in, toc, menu…)
Menus::items('main')liens du menu : label, href, path et external
Media::get(), Media::picture()image de la médiathèque, en WebP responsive
Content::path($entree)adresse publique d'un contenu

Exemple : afficher les traductions d'un article dans l'en-tête, à partir des liens créés dans l'éditeur.

<?php // layout.php, dans <header> (ajoute « use Eclair\Content; » en tête du fichier) ?>
<?php if (!empty($v['translations'])): ?>
  <nav class="lang-switch" aria-label="<?= e(Theme::t('read_in', $lang)) ?>">
    <?php foreach ($v['translations'] as $code => $t): ?>
      <a href="<?= e(path_to(Content::path($t))) ?>" hreflang="<?= e($code) ?>" lang="<?= e($code) ?>"<?= $t['id'] === ($v['id'] ?? '') ? ' aria-current="true"' : '' ?>><?= e(strtoupper($code)) ?></a>
    <?php endforeach ?>
  </nav>
<?php endif ?>

Le style de .lang-switch va dans le CSS personnalisé ou dans theme.css, jamais dans un attribut style.

Les contraintes de la CSP

Les pages publiques sont servies avec une Content-Security-Policy stricte, calculée par Security::headers(). Elle s'applique à tes gabarits comme au reste :

Performance des gabarits

Pour une page mise en cache, un gabarit ne s'exécute qu'au premier affichage : la suite est servie par le cache statique. Ce n'est pas une raison pour l'alourdir. Lis les listes dans l'index (Content::index(), Content::published()) plutôt que d'ouvrir chaque contenu, garde Media::picture() pour les images (dimensions, srcset WebP, chargement différé) et préfère les éléments HTML natifs, <details> ou l'attribut popover, à du JavaScript. Chaque Ko ajouté à theme.css est envoyé avec chaque page, puisque le CSS est inséré dans le HTML.

Garde aussi les appels Blocks::ea() de single.php (attributs data-ef) : ils n'apparaissent que dans l'aperçu de l'administration et permettent au mode Visuel de modifier sur place le titre, l'image à la une et « L'essentiel ».

Mises à jour, intégrité et manifest.php

La procédure de mise à jour supprime app/ puis le remplace : tes gabarits disparaissent avec lui. Organise-toi dès le premier jour.

  1. Range tes gabarits modifiés dans un dépôt Git, hors du site en ligne.
  2. Après chaque mise à jour, compare les gabarits d'origine de l'ancienne et de la nouvelle version, pour repérer une variable ajoutée ou un correctif.
  3. Reporte ces changements dans tes fichiers, teste en local, puis renvoie-les.
  4. Lance Sécurité › Intégrité des fichiers › Analyser maintenant : seuls tes fichiers doivent apparaître parmi les modifiés ou les inconnus.
# Ce qui a changé dans les gabarits d'origine entre deux versions
diff -ru eclair-1.7.0/eclair/app/theme eclair-1.8.0/eclair/app/theme

Le contrôle d'intégrité compare chaque fichier à son empreinte dans app/manifest.php. Une liste conforme à tes modifications est normale ; un fichier inattendu mérite une enquête. Pour un parc de sites, une variante plus robuste consiste à entretenir ta propre copie du dépôt et à produire ton archive avec php tools/build.php : son manifeste porte les empreintes de tes gabarits, et le contrôle reste propre. Les tests de rendu public de tests/run.php signaleront alors tes changements : c'est attendu, ils comparent au rendu d'origine.

L'exemple de cette académie

Éclair Academy est construite avec Éclair 1.8 et des gabarits du thème personnalisés. Le thème standard partage les mêmes menus entre toutes les langues ; nos gabarits ajoutent quatre comportements, en respectant les contraintes décrites plus haut (aucun script en ligne, aucun attribut style, rendu mis en cache comme d'habitude).

FichierRôle
app/theme/academy.phpClasse Academy : langue d'une adresse, filtrage des menus, icônes, parcours et position d'une leçon
app/theme/layout.phpEn-tête avec sélecteur de langue, menu mobile plein écran, pied de page avec les quatre parcours
app/theme/single.phpEn-tête de leçon (pastille, « Leçon 3 sur 9 », plan de la ligne), sommaire latéral, navigation précédente et suivante
app/theme/list.php et 404.phpRecherche et page introuvable dans la langue demandée
assets/site/academy.jsBouton « Copier » des blocs de code et repère de lecture du sommaire (chargé en différé)

Le cœur du filtrage tient en quelques lignes :

<?php
// app/theme/academy.php (extrait)
public static function langOf(string $path): string
{
    foreach (['en', 'es'] as $code) {
        if ($path === '/' . $code || str_starts_with($path, '/' . $code . '/')) {
            return $code;
        }
    }
    return 'fr';
}

/** Éléments d'un menu dans la langue de la page ; les liens externes s'affichent partout. */
public static function menu(array $items, string $lang): array
{
    $out = [];
    foreach ($items as $item) {
        $path = (string) ($item['path'] ?? '');
        if ($path !== '' && self::langOf($path) !== $lang) {
            continue;
        }
        [$icon, $label] = self::splitIcon((string) $item['label']);
        $out[] = $item + ['icon' => $icon, 'text' => $label];
    }
    return $out;
}

Pour que la page Sécurité reste au vert, le script de construction de l'académie recalcule l'empreinte de ces six fichiers dans app/manifest.php (les 32 premiers caractères de leur SHA-256, comme le manifeste d'origine). Après une mise à jour d'Éclair, il suffit de recopier ces fichiers depuis l'archive de l'académie, puis de vider le cache.

À retenir

Pour aller plus loin

Questions fréquentes

Mes gabarits survivent-ils à une mise à jour d'Éclair ?

Non : la mise à jour remplace tout le dossier app/, gabarits compris. Garde tes fichiers dans un dépôt Git et réapplique-les après chaque version, en vérifiant les changements d'origine.

Puis-je ajouter mon propre fichier JavaScript ?

La CSP accepte un fichier servi par ton domaine, jamais un script en ligne. Choisis bien son emplacement : assets/ est remplacé à chaque mise à jour, et le contrôle d'intégrité signale dans media/ tout ce qui n'est ni une image ni une police. Essaie d'abord une solution en HTML natif.

Comment ajouter un texte traduit absent de Theme::t ?

Theme::t() renvoie la clé elle-même quand elle n'existe pas. Déclare dans ton gabarit un petit tableau de textes par langue, indexé par $lang.

Le mode Visuel fonctionne-t-il avec mes gabarits ?

Oui, si tu gardes la sortie des blocs ($v['html']) et les attributs Blocks::ea() de single.php. Ils restent invisibles sur le site public.

Toutes les leçons du parcours