Design system

Palette, typographie, espace, formes, composants et thème sombre, la référence visuelle du produit.

Référence visuelle du produit. Décrit l'apparence, le ressenti et les règles visuelles, indépendamment de la technologie qui les implémente.

Philosophie

LRVO est un outil de travail, pas un produit marketing. L'interface disparaît pour laisser place au contenu.

Un seul principe directeur : ne jamais ajouter un élément sans raison. Chaque couleur, bordure, espace ou texte supplémentaire doit justifier sa présence. Le doute se résout toujours en faveur de la suppression.

Palette

Base

Par défaut, le produit est noir et blanc. Pas de teinte bleue ou violette dans les fonds. Le fond est blanc pur, le texte presque noir.

Un projet peut poser sa marque : une couleur d'action, une teinte de gris, une barre foncée. Elle passe par le thème du projet, jamais par une couleur écrite dans un composant. Les règles de sobriété qui suivent valent quel que soit le thème.

Jetons

Un composant n'écrit jamais une nuance de palette, seulement un rôle. text-fg-muted, pas text-neutral-500 dark:text-neutral-400. Le jeton porte à la fois le clair, le sombre et la marque du projet : une nuance écrite en dur reste noire quand le projet passe au bleu, et oublie le sombre une fois sur deux.

RôleJetonsUsage
Fondscanvas, surface, surface-muted, surface-hoverFond de page, cartes et champs, puces, survol d'une ligne ou d'une carte.
Textefg, fg-secondary, fg-muted, fg-subtlePrincipal, secondaire, discret, très discret.
Lignesline, line-strongSéparations, bordures appuyées.
Contrôlescontrol, control-hover, control-lineBouton secondaire, champ de saisie, ligne cliquable dans une carte.
Actionprimary, primary-hover, primary-fgBouton principal, élément sélectionné, barre de progression.
Accentaccent, accent-soft, accent-line, accent-fgLiens, cases cochées, sélection, cible de dépôt.
Barre et sidebarchrome, chrome-fg, chrome-fg-muted, chrome-line, chrome-hoverCoquille de l'admin uniquement.
Statutsdanger, success, warning, info, chacun avec -soft, -line, -fgVoir ci-dessous.

Les jetons control existent parce qu'un contrôle secondaire ne se comporte pas pareil dans les deux modes. En clair, il est transparent et sa bordure suffit à le dessiner. En sombre, une bordure seule ne le détache pas d'une carte presque noire : il prend un fond gris translucide. Aucun jeton de surface n'a ce comportement, d'où ce trio à part.

primary et fg se ressemblent en noir et blanc, et c'est le piège. fg est l'encre : du texte, un filet noir. primary est l'action : ce qui devient bleu quand la marque est bleue. Un titre en primary passerait à la couleur de marque, un bouton en fg resterait noir.

Le garde-fou npm run check:colors fait échouer le build du core dès qu'une nuance de palette, une couleur dark: ou une couleur arbitraire [#…] apparaît. Un module lance le même script sur son code : node ../core/scripts/check-colors.js dist. Les rares exceptions (couleurs catégorielles, couleurs choisies par l'utilisateur) sont déclarées une par une dans le script, avec leur raison.

Accents sémantiques

Quatre statuts, utilisés avec une grande parcimonie : pour les états, les alertes, les variations. Jamais comme couleur décorative, jamais en fond de section.

JetonCouleur par défautUsage
infoBleuInformation
successVertSuccès, actif, variation positive
warningAmbreAvertissement, en attente, brouillon
dangerRougeErreur, danger, suppression, variation négative

Chaque statut existe en trois déclinaisons : la couleur elle-même pour le texte, un fond pâle (-soft), une bordure légère (-line). Ces trois valeurs forment toujours un ensemble. On ne mélange pas les déclinaisons de statuts différents. Sur la couleur pleine, le texte prend -fg.

Données et catégories

Deux sortes de couleurs échappent aux jetons, et c'est voulu. Une couleur choisie par l'utilisateur est une donnée : la couleur d'un rôle, d'un service de réservation, un texte coloré dans l'éditeur. Elle s'affiche telle qu'elle a été saisie, en style inline. Une couleur catégorielle distingue des types entre eux, pas un état : les variantes purple, pink, orange de Badge, les icônes par type de fichier de Drive. La lier à la marque ferait se confondre deux catégories.

Ce qu'on n'utilise pas

  • Dégradés.
  • Couleurs de fond dans les sections, sauf survol et état actif.
  • Une couleur de marque écrite en dur dans un composant.
  • Transparence et flou, sauf l'overlay des modales.

Typographie

Police unique : IBM Plex Sans. Pour les codes, identifiants, montants et dates : IBM Plex Mono. Un projet peut changer ces deux familles avec l'option font du thème : les composants écrivent font-ui et font-code, jamais un nom de police.

RôleTailleGraisseNotes
Titre de page20 px600
Titre de section14 à 20 px600
Corps de texte13 px400
Texte secondaire12 px400Tableaux, listes
Labels et légendes12 px400 à 500
En-têtes de colonnes11 px500Majuscules, interlettrage +0.04em
Codes, identifiants, montants11 à 13 px400IBM Plex Mono

Taille minimale : 11 px. En dessous, on ne descend pas.

Règles d'écriture

  • Sentence case partout. « Tableau de bord », pas « TABLEAU DE BORD ». Seule exception : les en-têtes de colonnes de tableau, en petites majuscules.
  • Texte court et direct. Pas de phrase là où un mot suffit.
  • Les montants, les dates ISO et les identifiants sont toujours en monospace.

Voir Rédaction pour la voix des textes.

Espace

Base : 4 px. Tout espacement est un multiple de 4.

ValeurUsage
4 pxEspacement interne minimal, entre icône et texte
8 pxPadding compact, écart entre éléments proches
12 pxPadding interne des petits composants
16 pxPadding standard des cartes et panneaux
24 pxEspacement entre sections
32 pxPadding latéral du contenu principal
48 à 64 pxEspacement entre blocs majeurs

La densité est moyenne : ni étouffante, ni aérée. Un admin s'utilise plusieurs heures par jour, la densité confortable prime sur l'esthétique aérée.

Formes et bordures

Rayons

ContexteRayon
Badges, étiquettes, petits élémentsComplet
Boutons, champs de saisie8 px
Modales, panneaux flottants, tableaux, cartes12 px
Avatars, indicateurs rondsCercle

12 px est le rayon standard. On ne mélange pas les rayons dans un même composant.

Les deux rayons principaux sont des jetons : rounded-control pour les boutons et les champs, rounded-card pour les cartes, modales et panneaux. L'option radius du thème les fait varier ensemble.

Bordures

Toutes les séparations sont des bordures de 1 px solid. Pas d'ombre sur les cartes et surfaces statiques, pas de ligne de 2 px ou plus.

Les ombres existent uniquement pour ce qui flotte au-dessus de l'interface : menus déroulants, modales, toasts. Elles restent discrètes, juste assez pour indiquer la profondeur.

Structure de l'admin

┌─────────────────────────────────────────────┐
│ Sidebar 230px    │ Barre supérieure 48px      │
│                  ├─────────────────────────── │
│  Logo            │                            │
│                  │   Zone de contenu          │
│  Nav principale  │   padding 28px / 32px      │
│                  │                            │
│  ──────────────  │                            │
│  Paramètres      │                            │
└─────────────────────────────────────────────┘
  • Sidebar : fixe, 230 px, fond chrome, séparée par une bordure droite.
  • Barre supérieure : fixe, 48 px, fond chrome. Fil d'Ariane à gauche, emplacements des modules puis utilisateur à droite.
  • Contenu : canvas, gris très pâle. Les cartes et tableaux sont en surface, ce qui crée la séparation visuelle sans bordure de section. En thème sombre, le fond devient noir et les boîtes grises.

La barre et la sidebar portent les jetons chrome-*, distincts des surfaces. Par défaut elles prennent la surface en clair et le fond de page en sombre : en noir, la coquille se fond dans la page et seules les cartes se détachent. Avec chrome: 'dark', elles restent foncées quel que soit le mode, et le contenu garde ses surfaces claires.

Coquille topbar

Avec shell: 'topbar', la navigation quitte la sidebar pour la barre du haut, qui passe à 56 px. De gauche à droite : la marque, les sections de navigation, puis les emplacements des modules et le menu utilisateur. Le contenu prend toute la largeur.

┌───────────────────────────────────────────────────────────┐
│ Logo  Tableau de bord  Utilisateurs ▾  Projets    🔔  (JT) │
├───────────────────────────────────────────────────────────┤
│                                                           │
│   Zone de contenu, padding 28px / 32px                    │
│                                                           │
└───────────────────────────────────────────────────────────┘

Une section à un seul item est un lien, une section à plusieurs items un menu déroulant : c'est la règle de l'accordéon de la sidebar, posée à l'horizontale. L'item actif prend le fond chrome-hover. Sous 1024 px, le bouton de menu ouvre le tiroir de la sidebar. Il n'y a pas de fil d'Ariane : le titre de la page, dans AdminHeader, suffit à situer l'utilisateur quand la navigation est visible en permanence.

Cette coquille convient à une application aux sections peu nombreuses, un portail client par exemple. Au-delà de six ou sept sections, la barre déborde : la sidebar reste le bon choix.

En-tête de page

AdminHeader sépare deux zones : à gauche ce que la page est, à droite ce qu'on fait avec.

  • À gauche : le titre, le badge d'état, la description. Le badge se passe en prop badge et se pose à côté du titre. Un état n'est pas un contrôle : glissé dans le groupe d'actions, il casse l'alignement de la rangée et se lit comme un bouton mort.
  • À droite : le bouton retour puis les actions, toutes à la taille par défaut md. Une seule taille par rangée. Mélanger sm et md côte à côte donne trois hauteurs de boutons et c'est ce qui se voit en premier.

Trois contrôles maximum à droite, bouton retour compris : une action destructive, une action principale. Au-delà, les actions se regroupent dans un bouton split. Un bouton icône seule n'a pas sa place dans un en-tête : sans libellé à côté de voisins qui en ont, il ne se lit pas. Sa place est dans une ligne de tableau ou dans le menu du bouton split.

Pages publiques

Les pages servies aux visiteurs hors de l'admin, partage de fichiers, prise de rendez-vous, facture à payer, formulaire, passent toutes par le même cadre : PublicShell, exposé par @lrvo/core/shared/public. Une personne qui reçoit deux liens du même site ne doit pas avoir l'impression de changer de site en cours de route.

Deux dispositions. Un état qui tient en une phrase et au plus un champ, lien expiré, mot de passe demandé, lien fermé, prend une boîte centrée (layout="centered"), comme la page de connexion. Une vraie interface prend la page (layout="page").

Deux silhouettes d'en-tête

comfortablebar
Logo64 px, au-dessus du titre24 px, à gauche dans la barre
Titre30 à 36 px, 60014 px, 600, centré dans la barre
Positiondéfile avec la pagecollant en haut, bordure basse
Respiration au-dessus du contenu64 à 96 px32 à 48 px

La règle : une page qu'on ouvre pour regarder prend comfortable, une page qu'on ouvre pour faire quelque chose prend bar. Un partage de fichiers et une facture sont des documents : le grand titre les annonce comme une page d'accueil annonce un site. Un assistant de réservation et un formulaire sont des outils : l'en-tête y est de la hauteur prise à la tâche, et un visiteur qui a déjà lu le titre une fois n'a pas besoin qu'il occupe le tiers de son écran.

En silhouette bar, le titre de la barre est le titre de la page : il n'y a pas de second titre en dessous. Un sous-titre, s'il y en a un, devient la première ligne de la colonne de contenu.

Une page en silhouette bar qui déplace elle même le défilement doit poser une marge de défilement d'au moins 80 px (scroll-mt-20) sur sa cible, sans quoi celle ci se gare sous la barre.

Marque et pied de page

Le logo vient de la configuration du site, décliné par thème. Sans logo configuré, rien n'est rendu : un nom posé là où l'œil attend une image se lit comme un logo qui n'a pas chargé. Le nom du site reste dans le pied de page, où il est à sa place, suivi des mentions propres au module : lien privé, numéro de facture, numéros de taxes.

Trois zones verticales :

  1. Logo, hauteur 48 px, aligné sur la barre supérieure, séparé par une bordure basse.
  2. Navigation principale, défilante si nécessaire, padding 8 px autour des items.
  3. Items épinglés en bas, optionnels, séparés par une bordure haute, jamais dans la zone défilante.

Deux types d'items

  • Lien direct : une section qui ne contient qu'un item s'affiche comme un lien simple portant le titre de la section, sans chevron. Un post type « Actualités » sans taxonomie se résume donc à un bouton « Actualités ».
  • Section accordéon : une section à plusieurs items. Un bouton parent déploie ou replie les sous-items.

États

ÉtatApparence
InactifTexte chrome-fg-muted. Fond transparent.
SurvolFond chrome-hover. Texte chrome-fg.
Actif repliéFond chrome-hover. Texte chrome-fg. Graisse 500.
Actif ouvert, parentPas de fond, texte chrome-fg, graisse 500. Le fond actif est sur le sous-item.
Sous-item actifFond chrome-hover. Texte chrome-fg. Graisse 500.

Gabarit d'un item

Padding 7 px vertical et 10 px horizontal, rayon 8 px, icône 15 × 15 px à gauche avec un écart de 8 px, texte 13 px, transition 120 ms ease-out. Un badge numérique optionnel se place à droite : fond danger, texte danger-fg, 10 px, rayon 8 px.

Les sous-items n'ont pas d'icône et sont indentés de 25 px.

Accordéon

Le chevron de 12 × 12 px est à l'extrême droite du bouton parent. Il pivote de -90deg quand la section est repliée. L'animation du conteneur combine max-height et opacity, sur 120 ms ease-out.

Une section s'ouvre automatiquement au chargement si elle contient l'item actif. Toutes les autres sont repliées.

Mobile

Sous 1024 px, la sidebar sort du flux et se positionne en fixe sur toute la hauteur. Masquée par défaut, elle s'ouvre par un bouton externe, avec un overlay semi-transparent. Cliquer l'overlay ou naviguer la ferme. Au-delà de 1024 px, elle se referme automatiquement.

Boutons

VarianteApparenceUsage
PrimaireFond primary, texte primary-fgAction principale de la page
SecondaireFond control, bordure control-line, texte fg-secondaryActions secondaires, annulation
FantômeTransparent, sans bordure, texte fg-secondaryActions tertiaires
Fantôme pleinIdentique, sans anneau de focusBoutons très discrets dans une zone déjà interactive
DangerFond danger, texte danger-fgSuppression, action destructive
SuccèsFond success, texte success-fgConfirmation, validation
AvertissementFond warning, texte warning-fgAlerte, action risquée
dangerSoftFond danger-soft, bordure danger-line, texte dangerSuppression dans une liste ou un contexte secondaire
successSoftFond success-soft, bordure success-line, texte successValidation discrète
warningSoftFond warning-soft, bordure warning-line, texte warningAlerte secondaire

Le bouton primaire est le premier endroit où la marque du projet apparaît : avec une couleur primary, il la prend, et le texte posé dessus passe au blanc ou au noir selon le contraste.

Les variantes pleines sont la règle pour les actions principales : un bouton doit dominer visuellement le badge à côté. Les variantes *Soft servent quand la même action est répétée, par exemple une icône de suppression sur chaque ligne d'un tableau, ou quand le bouton est secondaire à côté d'un autre déjà plein. Les badges de statut gardent leur traitement teinté propre : ne pas les confondre avec ces variantes.

Il ne doit jamais y avoir plus d'un bouton primaire par section ou en-tête de page.

Tailles

TaillePadding horizontalPadding verticalTexte
sm10 px5 px12 px
md12 px7 px13 px
lg14 px9 px14 px

La taille par défaut est md.

Règles communes

Rayon 8 px, transition 120 ms ease-out, anneau de focus fin de la couleur de la variante. Une icône peut se placer à gauche ou à droite du texte. Un bouton icône seule a un padding uniforme. L'état de chargement remplace le contenu par un indicateur et désactive l'interaction. L'état désactivé passe à 50 % d'opacité, curseur interdit.

Bouton split

Quand plusieurs actions partagent la même intention avec des intensités différentes (Déployer, Démarrer sans rebuild, Redéployer sans cache), on n'aligne pas trois boutons primaires : visuellement ils se ressemblent et l'utilisateur ne sait plus lequel choisir. On utilise SplitButton : un bouton principal à gauche, un chevron à droite qui ouvre un menu de variantes. Chaque item peut porter une description qui explicite la nuance. Son panneau est ancré dans un portail, donc il reste lisible dans une modale ou un tableau défilant.

Une action d'intention opposée (Arrêter face à Démarrer) ne va pas dans le menu. Elle reste un bouton séparé, généralement en variante danger. Le SplitButton regroupe des variantes du même verbe, pas son inverse.

C'est aussi la sortie quand un en-tête déborde : plusieurs façons de remettre la même ressource au client (Renvoyer, Copier le lien, Télécharger le PDF) tiennent dans un seul bouton au lieu d'occuper trois places. Il accepte une icon comme Button, ce qui lui permet de remplacer une action principale sans que la rangée change d'allure.

Quand l'action principale disparaît selon l'état de la ressource, c'est l'item suivant du menu qui prend sa place. On ne laisse pas un chevron au-dessus d'un bouton mort.

Cartes et panneaux

Fond blanc, bordure 1 px, rayon 12 px, padding interne de 16 à 20 px, pas d'ombre. L'en-tête interne est séparé du contenu par une bordure horizontale, et le pied d'actions de formulaire également.

Les pages d'authentification font exception sur deux points : carte isolée au centre de l'écran, elle prend un padding de 32 px, et son pied est une bande séparée qui touche les deux bords. Leurs champs ne portent pas d'astérisque, parce que tous sont requis. Voir Apparence des pages d'authentification.

Champs de sélection

  • Select : <select> HTML natif. À utiliser quand la liste tient à l'écran et ne dépasse pas une dizaine d'entrées, statuts, types, langues. Pas de recherche.
  • Combobox : équivalent avec recherche. Trigger identique, champ de filtrage en tête, navigation au clavier. À utiliser dès que la liste devient longue, par exemple pour assigner un utilisateur à une ressource. Le filtre couvre label et value, et chaque option peut afficher un hint secondaire grisé. Son panneau est ancré dans un portail, donc la liste passe par-dessus une modale au lieu d'être rognée par son corps défilant.

Les deux respectent les mêmes conventions de label, description et erreur qu'Input. Pas de composant enveloppant.

Boutons d'action des formulaires

Le placement dépend du contexte.

Formulaire pleine page : utiliser UnsavedChangesBar.

  • La barre apparaît dès qu'une modification est détectée et disparaît au retour à l'état initial.
  • Annuler est secondaire et ramène le formulaire à son état initial sans naviguer.
  • Enregistrer est primaire et désactivé pendant l'enregistrement.
  • Pas de bouton de fermeture : tant qu'il y a des modifications, l'utilisateur choisit explicitement.
  • Le hook sous-jacent bloque toute navigation par lien tant qu'il reste des modifications, et fait se secouer la barre à chaque tentative.
  • Après un enregistrement réussi, rester sur la page. Pour une création, rediriger vers la page d'édition de la ressource créée : l'utilisateur reste avec la ressource, pas dans le listing.
  • Aucun bouton inline en sidebar ou en pied de carte.

Le détail de l'API et la gestion de la navigation interne non liée sont dans Composants et Hooks.

FloatingActionBar reste réservée aux actions d'une sélection. Elle apparaît dès le premier élément sélectionné quand la sélection est explicite, cases à cocher à l'appui : l'utilisateur qui vient de cocher attend une suite. Elle ne porte que les actions qui ont un sens sur un lot, le reste vivant dans le menu de la ligne.

Formulaire en modale : garder les boutons dans le pied de la modale. Une modale est déjà contextualisée, l'utilisateur ne peut pas la perdre, et sa croix sert d'annulation explicite.

Tableaux

  • En-têtes de colonnes en 11 px, majuscules, couleur secondaire.
  • Lignes à fond blanc, survol légèrement grisé.
  • Bordure horizontale entre les lignes, pas sur la dernière.
  • Données numériques, identifiants et dates en IBM Plex Mono.
  • Actions à l'extrême droite, en boutons icône discrets.
  • Au-delà de deux actions, ou dès qu'une est destructive, elles passent dans un DropdownMenu plutôt que de s'aligner. Six icônes dans une cellule ne se lisent pas, et une suppression alignée avec les autres se clique par erreur.
  • Une liste où le clic droit est le geste attendu, un explorateur de fichiers par exemple, peut offrir le même menu au pointeur avec ContextMenu. En plus du bouton à trois points, jamais à sa place : une action qui n'existe qu'au clic droit ne se découvre pas et ne s'atteint pas au toucher. Voir Composants.
  • Pas de fond alterné.
  • Un rechargement ne vide pas le tableau. Les squelettes sont réservés au premier affichage.
  • Une liste pleine page pose une hauteur plancher, minHeight sur Table. Un tableau dont la hauteur suit le nombre de lignes se réduit à un bandeau dès que la liste est courte, et saute d'une page à l'autre. Les squelettes remplissent cette hauteur, l'état vide s'y centre, et les lignes gardent la leur. Voir Composants.
  • Une sélection se fait à la case à cocher, en tête de ligne, avec une case dans l'en-tête pour la page affichée. Le clic simple sert à ouvrir : une sélection qui ne s'annonce pas ne se découvre pas, et la case est ce qui l'annonce. Ctrl/Cmd + clic et Maj + clic sélectionnent aussi, mais comme un raccourci pour qui le connaît déjà, jamais comme le seul chemin. Voir Composants.
  • Un glisser-déposer double une action du menu, il ne la remplace pas. Déplacer une ligne à la souris est le geste attendu d'un explorateur de fichiers, mais il ne se voit pas avant d'être tenté, il n'existe pas au toucher, et il ne s'atteint pas au clavier. L'action doit donc rester dans le menu de la ligne et dans la FloatingActionBar. La cible sous le curseur se surligne, et le pointeur emporte une pastille qui dit ce qu'il tient.

Style par défaut des cellules. Une colonne sans render est automatiquement enrobée dans le style monospace canonique. Réserver render aux cas qui demandent une composition. Voir Composants.

Badges de statut

Petits éléments inline indiquant un état. Toujours trois éléments ensemble : fond pâle de la couleur sémantique, bordure légère, texte de la couleur sémantique. Rayon complet, texte 11 px.

États interactifs

ÉtatApparence
SurvolFond surface-hover : 4 à 5 % plus sombre en clair, un cran plus clair en sombre, jamais vers le noir du fond de page.
Actif ou focusLa bordure de 1 px prend la couleur du texte principal
Désactivé50 % d'opacité, curseur interdit
Chargement, premier affichageSquelette de la forme du contenu attendu
Chargement, revalidationLe contenu reste en place, atténué à 60 %, contrôles verrouillés

Toutes les transitions durent 120 ms, courbe ease-out. Aucune animation décorative.

Les widgets ARIA marquent l'état désactivé avec aria-disabled, pas avec disabled. Checkbox et Switch sont des <button> porteurs d'un rôle. L'attribut disabled fait partie de l'état de formulaire que le navigateur restaure au rechargement d'une page : une case rendue désactivée par le serveur, parce que le tableau est encore vide, peut ainsi revenir active avant que React ne prenne la main, et l'hydratation signale alors un écart qu'elle ne répare pas. aria-disabled porte la même information sans passer par cet état, et laisse le contrôle atteignable au clavier. Le gestionnaire de clic reste responsable de bloquer l'action.

Un bouton ordinaire garde disabled : c'est l'attribut juste pour un contrôle de formulaire, et son état ne dépend pas d'une donnée arrivée après le rendu serveur.

Un contenu déjà affiché ne disparaît jamais pour être rechargé. Le squelette répond à l'absence de donnée, pas à l'attente d'une donnée fraîche. Vider une vue le temps d'un aller-retour réseau se lit comme un clignotement, et fait sauter la mise en page à chaque tri ou changement de page.

Ce qu'on n'utilise jamais

  • Dégradés sur les fonds ou les boutons.
  • Cartes à bordure colorée uniquement sur le côté gauche.
  • Ombres internes.
  • Gras utilisé pour une emphase décorative.
  • Icônes sans fonction claire.
  • Statistiques ou chiffres inventés pour remplir l'espace.
  • Couleurs de fond dans les sections de contenu.

Thème sombre

Le thème sombre est une inversion calibrée : les fonds passent au noir complet, les zones par-dessus au noir-gris, les textes s'éclaircissent, et les accents gagnent légèrement en luminosité pour rester lisibles. La structure, les espaces et les proportions restent identiques.

Le sombre est porté par les jetons, pas par les classes. Chaque jeton a une valeur claire et une valeur sombre, et la classe dark sur <html> fait basculer les deux ensemble. Un composant écrit bg-surface text-fg, jamais bg-white dark:bg-neutral-900. C'est ce qui rend l'oubli impossible : sans variante dark: à écrire, il n'y en a pas à oublier. Le variant dark: ne sert plus qu'à ce qui n'est pas une couleur, comme afficher un logo propre au sombre.

Un projet peut n'offrir qu'un mode avec l'option modes du thème. Toutes les bascules disparaissent alors, dans l'admin comme sur les pages publiques.

L'implémentation est décrite dans Thèmes.

Sur cette page