Aller au contenu
Retour à Apprendre
Design26 juillet 20267 min de lecture

Concevoir un système de design pensé d'abord pour le mode sombre

SIGNAL, le système sur lequel tourne ce site : pourquoi le sombre est l'acte principal, pourquoi les jetons portent des rôles plutôt que des valeurs, pourquoi le noir pur est un piège et pourquoi il n'y a qu'un seul accent.

Kushagra SharmaFondateur et ingénieur produit

Ce site repose sur un système de design appelé SIGNAL : une rampe de graphite, un unique accent vert citron électrique et deux actes, sombre et clair. Le sombre a été conçu en premier et le clair en second, délibérément, dans cet ordre. Voici le raisonnement derrière les jetons, les pièges de contraste que nous avons rencontrés, et pourquoi il y a exactement une couleur d'accent. Tout ce qui est cité ici vit dans un seul fichier, src/app/globals.css, de sorte que rien n'est à prendre sur parole.

Penser d'abord en sombre est une contrainte, pas une couche de peinture

La plupart des systèmes de design sont construits en clair puis inversés. On définit une palette sur fond blanc, on la livre, et trois sprints plus tard quelqu'un greffe un thème sombre en retournant quelques variables. Cela paraît correct dans le fichier de maquette et s'effondre en production : des panneaux d'un noir pur, un texte gris qui disparaît, des ombres sans effet parce qu'il n'y a pas de lumière pour les projeter.

Construire dans l'autre sens force les meilleures décisions tôt.

  • Vous rencontrez d'abord les modes de défaillance du faible contraste, quand ils sont encore peu coûteux à corriger.
  • L'élévation doit s'exprimer par la couleur de surface plutôt que par l'ombre, ce qui est de toute façon un modèle plus honnête.
  • L'accent est choisi sur un fond sombre, où une couleur saturée se comporte très différemment que sur du blanc.

Le cadre des actes n'est pas décoratif. Un acte a son propre éclairage et sa propre ambiance, mais c'est la même histoire avec les mêmes personnages. Les deux actes partagent la structure et l'intention. Ce qui change, c'est la mise en scène.

Les jetons portent un sens, pas des valeurs

L'erreur qui tue la gestion des thèmes consiste à nommer les jetons d'après leur apparence. --gray-900. --blue-500. Dès que vous le faites, les deux thèmes ne peuvent plus partager une seule ligne de code de composant, car le composant connaît désormais une couleur littérale.

Chaque jeton sémantique de SIGNAL est nommé d'après sa fonction. Un composant demande --surface, --muted ou --border. Il ne demande jamais un code hexadécimal et ne sait jamais dans quel acte il se trouve. C'est l'acte qui décide de ce que ces rôles deviennent.

Un composant doit décrire ce dont il a besoin, pas l'apparence qu'il veut avoir. « Donne-moi la couleur de la surface surélevée » est un contrat. « Donne-moi #1a1a1a » est un passif.

Il y a deux couches en dessous, et les garder séparées est ce qui rend le système maîtrisable. Une rampe brute contient les valeurs (--color-n-850, --color-lime-500) et rien en dehors du fichier de jetons ne peut s'y référer. Une couche sémantique associe des rôles à cette rampe, une fois par acte, et c'est la seule couche que les composants voient.

Le noir pur est un piège

Le premier réflexe dans un thème sombre est de prendre #000000. Cela vous coûte trois choses distinctes.

Sur les écrans OLED, le noir pur sous un texte lumineux produit un effet de traînée visible quand l'œil se déplace, si bien que les bords du texte semblent vibrer. Il ne laisse aucune marge en dessous : si la base est noire, vous avez dépensé le bas de votre plage avant de commencer, et chaque niveau d'élévation doit grimper. Et il aplatit l'ombre, car une ombre n'est qu'une zone plus sombre et rien n'est plus sombre que le noir.

Le canevas de SIGNAL est #0a0b0d, à quelques points du noir, et l'échelle de surfaces s'en écarte vers le haut plutôt que dessous : #15171c est la surface surélevée sur laquelle une carte est dessinée, et #1c1f25 est le niveau en retrait utilisé à l'intérieur d'une carte, pour un bloc de code ou une citation mise en exergue. La profondeur se lit par la seule couleur de surface, ce qui permet à l'ombre d'être un accent plutôt qu'une béquille.

L'autre piège est le contraste dans le mauvais sens. Le minimum WCAG de 4,5:1 pour le texte courant est un plancher, pas un objectif, et sur un fond très sombre un contraste maximal est franchement désagréable. Le blanc pur sur #0a0b0d donne 19,69:1 et fatigue sur une longue session ; le texte courant de SIGNAL est donc #e9ecef, qui atteint 16,60:1 sur la même base et se supporte plus facilement. Les deux chiffres sont le rapport de contraste standard entre les deux valeurs hexadécimales citées dans ce paragraphe. N'importe quel vérificateur de contraste les reproduira.

Les jetons, et comment ils basculent

Les composants référencent des variables sémantiques. Un acte est une portée qui redéfinit ce que ces variables deviennent. Aucun code de composant ne change d'un acte à l'autre.

/* Acte un : le sombre est le défaut, déclaré sur :root */
:root {
  --bg:        #0a0b0d; /* quelques points au-dessus du noir, jamais #000 */
  --surface:   #15171c; /* en relief : la carte, le panneau, le contrôle */
  --surface-2: #1c1f25; /* en creux : le niveau utilisé À L'INTÉRIEUR d'une surface en relief */
  --text:      #e9ecef; /* volontairement pas du blanc pur */
  --muted:     #969da8;
  --border:    #2c313a;
  --accent:    #c8ff3d; /* lime électrique, réglé pour un fond sombre */
}

/* Acte deux : le clair est dessiné, pas obtenu en inversant l'acte un */
[data-act="light"] {
  --bg:        #f6f7f9;
  --surface:   #ffffff;
  --surface-2: #e9ecef; /* même rôle en creux, atteint en allant vers plus SOMBRE */
  --text:      #0f1115;
  --muted:     #565c66;
  --border:    #d8dce2;
  --accent:    #97d60f; /* même famille, plus dense : le lime se délave sur le blanc */
}

Dans le vrai fichier, chacune de ces valeurs provient d'un échelon nommé de la rampe plutôt que d'un littéral, de sorte que le même graphite n'est jamais saisi deux fois. Les valeurs résolues sont montrées ici parce que ce sont elles sur lesquelles il faut réellement raisonner.

Deux choses méritent d'être notées. --surface-2 tient un seul rôle dans les deux actes, le niveau en retrait à l'intérieur d'une surface surélevée, mais il y arrive par des directions opposées : plus clair que --surface en sombre, plus foncé en clair. Le rôle est stable et la physique s'inverse en dessous. Et l'accent n'a pas la même valeur dans les deux. #c8ff3d est net sur un quasi-noir et se délave sur du blanc ; l'acte clair passe donc à un #97d60f plus dense, de même teinte. Les rôles restent constants, les valeurs sont ajustées par acte.

Un seul accent, et l'exception qui confirme la règle

La tentation, dans tout système de design, est de livrer un arc-en-ciel : une couleur primaire, une secondaire, un bleu informatif, un dégradé pour la page marketing. SIGNAL livre un seul accent et le traite comme une ressource rare.

  • Il rend la hiérarchie sans ambiguïté. Quand une seule chose est colorée, c'est elle qui compte. Un second accent ouvre une compétition pour l'attention que le lecteur doit arbitrer.
  • Il reste maîtrisable d'un acte à l'autre. Ajuster une teinte sur deux fonds est un travail que l'on termine. En ajuster cinq est une source permanente d'incohérence.
  • Il laisse aux couleurs sémantiques, succès, avertissement et danger, la place de vouloir réellement dire quelque chose, parce qu'elles ne se disputent pas le regard du lecteur avec la décoration.

L'exception est la partie instructive. L'anneau de focus est son propre jeton plutôt qu'un alias de l'accent, parce qu'un indicateur de focus est une cible de contraste non textuelle : WCAG 2.2 demande 3:1 par rapport au fond sur lequel il se trouve, et l'accent de l'acte clair n'y atteint que 1,64:1. L'anneau de focus de l'acte clair utilise donc un vert chartreuse bien plus foncé, #3f5c00, qui franchit l'exigence avec 7,14:1.

Voilà à quoi ressemble concrètement la discipline de l'accent unique. Pas une valeur hexadécimale partout, mais un seul sens, exprimé avec la valeur qui passe le seuil dans le contexte où elle tombe. Quand un jeton doit diverger, la raison est écrite à côté dans le fichier, car une divergence que personne n'a documentée est une divergence que quelqu'un copiera plus tard.

Ce que la discipline rapporte

Si la couche sémantique est juste, la gestion des thèmes cesse d'être un refactoring pour devenir un problème de données. Un troisième acte, une véritable mise en scène à fort contraste, est une nouvelle portée avec de nouvelles valeurs plutôt qu'une seconde bibliothèque de composants. La retenue se lit comme de l'assurance pour la même raison structurelle : un système qui utilise la couleur avec parcimonie donne l'impression de savoir ce qui compte, parce qu'il a dû décider.

C'est tout ce que rapporte le fait d'écrire les jetons comme des rôles. Les composants n'étaient jamais la partie qui devait changer.

Partager cet article

Besoin de faire construire un produit ?

Décrivez le problème avec vos propres mots. Vous aurez une réponse franche sur ce que demande réellement sa construction et sur le déroulement d'une mission.