Aller au contenu

Pourquoi un accent d'instance piloté serveur

Kirexo distingue deux réglages d'apparence qui se ressemblent mais n'ont pas du tout le même statut : la couleur d'accent, qui est un réglage d'instance rendu côté serveur, et le thème clair/sombre, qui reste une préférence par visiteur gérée côté client. Cette page explique pourquoi cette frontière est tracée ainsi, et quelles conséquences techniques elle a eues — notamment le dégraissage du script accent.js. Pour la description factuelle des fichiers, attributs et valeurs, voir la référence Design system : tokens, thèmes et accent.

L'accent est un réglage d'instance, pas une préférence de visiteur

La couleur d'accent fait partie de l'identité visuelle de l'instance Kirexo, au même titre que son nom ou son favicon. Quand l'administrateur d'une instance choisit « Bordeaux », il décide de l'apparence de sa plateforme — pour tous ses lecteurs, pas seulement pour lui.

C'est pourquoi l'accent est persisté par une entité Setting singleton (une seule ligne en base), et non dans le navigateur de chaque visiteur. Une seule valeur fait autorité, partagée par tout le monde. À l'inverse, laisser chaque visiteur choisir sa propre couleur d'accent reviendrait à diluer l'identité de l'instance : deux lecteurs verraient deux « Kirexo » différents, ce qui n'a pas de sens pour une marque.

Le statut singleton porte cette intention dans le modèle : il n'existe pas de notion d'« accent du visiteur ». La seule question est « quel est l'accent de cette instance ? », et la réponse est unique.

Pourquoi le rendre côté serveur

L'accent étant une valeur d'instance unique, il est lu au rendu de chaque page et injecté directement dans le HTML servi :

<html lang="fr" data-theme="light" data-accent="{{ platform_accent() }}">

Deux raisons motivent ce rendu serveur plutôt qu'une application en JavaScript après chargement.

Cohérence multi-appareils. Comme la source de vérité est la base de données, l'accent est identique quel que soit l'appareil, le navigateur ou la session. Rien ne dépend d'un état local : ouvrir l'instance depuis un téléphone, un poste public ou une session fraîche donne exactement la même couleur. Un changement décidé par l'administrateur se propage à tout le monde au rechargement, sans qu'aucun visiteur n'ait à « resynchroniser » quoi que ce soit.

Pas de flash de couleur au chargement. Si l'accent était appliqué par un script après le rendu, la page s'afficherait d'abord avec une couleur par défaut (ou neutre), puis basculerait brutalement vers la bonne teinte une fois le JavaScript exécuté — un flash visible et désagréable. En posant data-accent directement dans le HTML, la bonne couleur est présente dès le premier octet rendu : aucune transition parasite.

Pourquoi accent.js a été dégraissé

Le script accent.js fourni par le design system, dans sa version source (design-system/accent.js), faisait trois choses : il stampait le logomark SVG, il construisait les sélecteurs d'accent, et il persistait l'accent choisi dans localStorage (clé kirexo-accent) pour le réappliquer au chargement de chaque page.

Cette dernière partie entre en conflit direct avec un accent d'instance rendu côté serveur. Si le script réappliquait l'accent depuis le localStorage du visiteur, il écraserait la valeur data-accent posée par le serveur dès le chargement de la page. Un visiteur ayant un jour cliqué sur « Prune » continuerait de voir « Prune » même après que l'administrateur a basculé l'instance sur « Forêt » — exactement l'inverse de la cohérence recherchée.

La copie de travail assets/js/accent.js est donc dégraissée de cette persistance et de cette ré-application : la valeur serveur data-accent reste la seule source de vérité au chargement. Les parties utiles au reste de l'application — le stampage du logomark et la fonction d'application de l'accent — sont conservées.

Le choix de l'accent par l'administrateur se fait désormais sur la page Réglages (/admin/reglages), qui écrit la valeur dans l'entité Setting côté serveur — et non dans le localStorage du navigateur. Le déroulé est décrit dans le how-to Définir la couleur d'accent de la plateforme ; la mécanique de la page est en Référence des espaces et dashboards.

Pourquoi le thème reste, lui, une préférence locale

Le thème clair/sombre relève d'un tout autre besoin. Préférer le sombre ou le clair dépend de l'environnement du lecteur — luminosité ambiante, fatigue visuelle, heure de la journée, réglage système de son appareil. Ce n'est pas une décision de marque, c'est un confort personnel.

Le thème est donc géré côté client : assets/js/theme.js lit et écrit la clé localStorage kirexo-theme. Chaque visiteur garde son réglage, propre à son navigateur, sans incidence sur les autres ni sur l'instance.

Un sélecteur tri-état, pas une simple bascule

Là où l'accent se choisit sur la page Réglages admin, le thème se choisit dans le sélecteur présent dans l'entête partagé (templates/components/Header.html.twig), affiché sur tous les écrans — y compris l'accueil public, puisqu'il ne suppose aucune authentification. C'est le pendant naturel d'une préférence de visiteur : à portée de clic, sur chaque page, sans détour par un espace connecté.

Le sélecteur n'offre pas un simple interrupteur clair/sombre, mais trois modes : light, dark et system. La nuance est délibérée. Forcer le clair ou le sombre, c'est figer une palette quelle que soit l'heure ou l'appareil ; choisir system, c'est déléguer la décision au réglage clair/sombre de l'OS. Stocker un booléen « sombre oui/non » perdrait cette troisième intention : la clé kirexo-theme mémorise donc le mode (light | dark | system), pas le thème résolu. Le mode par défaut, en l'absence de tout choix, est system.

Pourquoi « système » suit l'OS en direct

Le mode system ne se contente pas de lire prefers-color-scheme au chargement : il réagit en temps réel aux changements de l'OS, via un écouteur sur matchMedia('(prefers-color-scheme: dark)'). Si l'appareil bascule en sombre le soir, l'interface suit immédiatement, sans rechargement. Ce serait incohérent autrement : « suivre le système » qui ne suivrait qu'au premier affichage ne serait pas vraiment suivre le système. Les modes light et dark, eux, ignorent volontairement ce signal — l'utilisateur a explicitement forcé une palette.

Pourquoi synchroniser entre onglets

Une préférence de visiteur vit dans localStorage, qui est partagé par tous les onglets d'une même origine. Si le thème n'était appliqué qu'à l'onglet où l'on clique, les autres onglets ouverts afficheraient encore l'ancien thème jusqu'à un rechargement — une incohérence visible pour un même visiteur, dans un même navigateur. theme.js écoute donc l'event storage, émis dans les autres onglets quand kirexo-theme change, et y réaligne le thème. Le choix se propage à toutes les fenêtres ouvertes de l'instance.

Pourquoi appliquer le thème avant le premier paint

Le script de thème est chargé en module différé (via app.js / l'importmap) : exécuté tel quel, il s'appliquerait après le premier rendu, et un visiteur en sombre verrait un flash clair le temps que le module s'exécute — le même défaut que celui évité pour l'accent. Pour l'empêcher, un court script inline en tête de templates/base.html.twig résout le thème effectif (même logique que theme.js, volontairement minimale) et pose data-theme sur <html> avant le premier paint. La bonne palette est donc présente dès le premier octet, sans transition parasite.

La distinction est assumée et symétrique :

Réglage Statut Source de vérité Mécanisme
Accent Identité d'instance Base de données (Setting singleton) Rendu serveur (data-accent)
Thème Confort du visiteur Navigateur (localStorage kirexo-theme, mode light/dark/system) Script client (data-theme), suivi OS live + synchro onglets

Mettre l'accent côté serveur et le thème côté client n'est pas une incohérence : c'est le reflet fidèle de ce que chaque réglage représente. L'un appartient à l'instance, l'autre au lecteur.

E-mails au design system

Les e-mails HTML de Kirexo adoptent le design system, mais par un chemin différent de celui des pages web — parce qu'un e-mail est un media à part.

Pourquoi pas var() ni feuille externe

Les pages web s'appuient sur les variables CSS du DS (var(--accent), var(--ink)…) et une feuille de styles. Les clients mail (Gmail, Outlook, Apple Mail…) ne sont pas des navigateurs : la plupart ne supportent ni les variables CSS, ni les feuilles externes, ni <style> fiable. La seule technique robuste est le style inline, posé directement sur chaque élément.

Le layout partagé templates/email/base.html.twig est donc écrit en styles inline, et non en classes du DS. Mais on ne réécrit pas les couleurs à la main pour autant : elles restent dérivées des tokens du design system, via un point unique côté serveur.

email_palette(space) : une palette matérialisée, source unique

La fonction Twig email_palette(space) (extension App\Twig\EmailPaletteExtension, exécution déléguée à App\Twig\Runtime\EmailPaletteRuntime) renvoie une palette en valeurs concrètes (hex), prête à inliner. Elle matérialise les tokens du DS en palette claire — le dark-mode en e-mail n'étant pas fiable, l'<head> déclare d'ailleurs color-scheme: light only.

L'argument space décide de l'accent :

  • user — reprend l'accent d'instance courant (le même que celui des pages, résolu depuis l'entité Setting). L'e-mail porte l'identité visuelle choisie dans les réglages.
  • admin — accent figé sur graphite, l'identité neutre du back-office, quel que soit l'accent d'instance. Le layout ajoute en plus une pastille « Admin » dans l'en-tête (p.isAdmin).

Le runtime étant lazy, le Setting d'instance n'est requêté que lorsqu'un e-mail est réellement rendu — jamais au rendu d'une page web. C'est la même source de vérité que l'accent serveur décrit plus haut : on ne duplique pas la palette, on la projette en hex pour le contexte e-mail.

Premier e-mail concerné

L'e-mail de réinitialisation de mot de passe (templates/reset_password/email.html.twig) est le premier à en bénéficier : il étend email/base.html.twig et expose un CTA en bouton accent. C'est le space (transmis au rendu de l'e-mail) qui détermine s'il porte l'accent d'instance (espace utilisateur) ou l'identité graphite (espace admin). Pour le parcours fonctionnel, voir Réinitialiser son mot de passe ; pour faire arriver l'e-mail en local, Recevoir les e-mails en développement.

Voir aussi