|
Frederic Oglaza
|
Senior Product Designer
|

Les design tokens à l'ère Claude

L'IA permet désormais au designer d'implémenter efficacement un système de design tokens complexe.
design-systemdesign tokensaccessibilitéopen source
Les neuf palettes du système, onze crans chacune, toutes calées sur la même courbe de luminosité

// ON NE LES ÉCRIT PLUS À LA MAIN

Un design system, c'est environ 1600 tokens. Écrits à la main, ils divergent : c'est une question de temps, pas de rigueur.
Ici, aucun n'est écrit. Des tables de mapping posent les règles, un script écrit les tokens. Et écrire ce script coûte aujourd'hui moins cher que d'écrire les tokens eux-mêmes — c'est ce que l'IA déplace.

On ne maintient plus des valeurs, on maintient des définitions.

1131
tokens de composant, aucun écrit à la main
17
composants générés depuis quatre tables
1 ligne
pour changer l'identité d'un projet
Grille de badges : les six rôles en lignes, les états défaut, survol et désactivé en colonnes, tous rendus depuis les tokens générés
Ce que produit la génération
Six rôles, trois états. Aucune ligne n'a été écrite par rôle : la variante déclare les slots qu'elle consomme, le rôle n'est qu'un paramètre.

// DÉFINIR AU LIEU D'ÉCRIRE

La définition tient en quatre couches, et chacune ne parle qu'à la suivante.
Primitives
Le catalogue brut : palettes, tailles, polices, durées. Aucune sémantique.
Brand
La sélection du projet : quelle palette, quelle police, pour quel rôle.
Sémantique
Les rôles, déclinés par mode : clair/sombre, desktop/mobile.
Composant
L'usage : dix-sept composants qui ne connaissent que des rôles.
Une seule règle les tient : une couche ne référence que celle du dessous. Un composant ne connaît donc jamais une couleur, seulement un rôle — et un grep suffit à le prouver.
// la chaîne de résolution d'un bouton
--color-button-filled-primary-default-background: var(--color-role-primary-main);
--color-role-primary-main:                        var(--color-brand-primary-600);
--color-brand-primary-600:                        var(--color-violet-600);
--color-violet-600:                               #9500ee;
Table des tokens du bouton : chaque token de composant, le rôle sémantique qu'il consomme, et la valeur finalement résolue
Ce que consomme un bouton
La valeur n'apparaît qu'au bout de la chaîne. Changer de thème ne redéfinit que le maillon sémantique.

// UN THÈME EN UNE LIGNE

Changer l'identité d'un projet, c'est éditer une ligne et relancer la génération. Rien d'autre ne bouge : ni les rôles, ni les composants. Tous les boutons, liens et états suivent.
// l'identité d'un projet, et sa police
const BRAND = {
  primary: 'turquoise',   // ← était 'violet'
  neutral: 'slateBlue',
  success: 'green', warning: 'orange',
  danger:  'red',   info:    'blue',
};

const BRAND_FONT = { sans: 'geist', serif: 'georgia', mono: 'jetbrainsMono' };
Ce qui rend le swap sans danger, c'est que les six rôles — primary, neutral, success, warning, danger, info — exposent exactement les mêmes slots. N'importe quelle palette peut jouer n'importe quel rôle.
Grille des six rôles en lignes et de leurs neuf slots en colonnes, rendue en thème sombre : chaque case affiche la couleur résolue
Six rôles, les mêmes slots
Un composant qui sait afficher primary sait afficher les cinq autres : passer un bouton en danger ne demande pas un token de plus.
Un thème ne se redessine pas, il se redéclare.

// EN CODE COMME DANS FIGMA

Rien n'oblige cette définition à s'arrêter au CSS. Les variables de Figma se décrivent de la même façon : une IA branchée sur le fichier les y écrit depuis les mêmes tables. Même source, deux destinations.
Panneau des variables de Figma : cinq collections — Brand, Color, Primitives, Size, Typography — et le groupe des rôles, chaque slot aliasé sur une variable de marque, en mode clair et en mode sombre
Les mêmes tokens, dans Figma
489 variables importées sans qu'aucune soit saisie à la main : 210 primitives, 93 de marque, 89 sémantiques, 60 typographiques, 37 de taille.
Le contrat est le même des deux côtés : 54 variables de rôle, six rôles de neuf slots, aliasées sur la couche brand comme en CSS — et déclinées en clair et en sombre.

// UNE DOC QUI NE SE PÉRIME PAS

La documentation est la troisième destination. Elle n'est pas écrite à côté du système : elle sort du même build. Chaque régénération la remet à jour, et une page corrigée à la main serait écrasée à la suivante.
Page Button de la documentation générée : à gauche la navigation des dix pages, à droite les variantes filled et outlined déclinées sur les six rôles et leurs quatre états
Le bouton dans la doc générée
Deux variantes, six rôles, quatre états — ici en thème clair, la doc bascule en sombre d'un clic. Ce ne sont pas des captures : c'est le CSS généré, appliqué dans la page.
La documentation cesse d'être un chantier à maintenir : elle se régénère avec le système. Dix pages autonomes, publiées telles quelles.

// CE QUE LA COURBE GARANTIT

Un système qu'on régénère doit tenir ce qu'il promet. Avec des rampes dessinées à la main, la promesse était fausse : le cran 500 valait 55 % de luminosité en gray et 89 % en turquoise. Un bouton primaire en turquoise sortait illisible.
1,85:1
le bouton turquoise, avec des rampes dessinées à la main
4,60:1
le même bouton, une fois les rampes calculées en OKLCH
Les onze crans sont désormais calculés sur une courbe de luminosité commune à toutes les palettes. À cran égal, deux palettes ont la même luminosité perçue : c'est ce qui autorise à échanger les couleurs sans vérifier les contrastes un par un. Une palette se définit par une teinte et un pic de chroma ; le reste se calcule.
// ajouter une palette
const PALETTES = {
  violet:    { hue: 304, chroma: 0.297 },
  turquoise: { hue: 181, chroma: 0.161 },
  // …
};

// LA TYPO SUIT LE MÊME CHEMIN

Dix rôles typographiques, six slots chacun, et une règle qui décide de tout : un rôle porte sa taille. Un rôle incomplet oblige à chercher ailleurs, donc on le contourne — c'est ce qui s'était produit, et les composants sortaient dans la police du navigateur.
Les tailles vivent dans le thème, parce qu'elles sont la seule dimension de la typographie qui change entre desktop et mobile. Le rôle pointe dessus au lieu de la copier.
Personne ne connaît la media query, et tout le monde la respecte.
// le rôle pointe sur la taille au lieu de la copier
--typography-h1-size: var(--font-size-h1);

@media (max-width: 768px) {
  --font-size-h1: 32px;
}
DesktopMobile, sous 768px
Desktop
h1 — 48px
Mobile, sous 768px
32px
Desktop
h2 — 32px
Mobile, sous 768px
24px
Desktop
h3 — 24px
Mobile, sous 768px
20px
Desktop
lead — 20px
Mobile, sous 768px
18px
Desktop
body, small, caption
Mobile, sous 768px
inchangés
// note
Seul le haut de l'échelle se comprime : le texte de lecture ne descend jamais. Les contrôles font l'inverse et grandissent — un bouton md passe de 40 à 52px sous 768px, pour une cible tactile d'au moins 44px (WCAG 2.5.5).

// LIMITES ET SUITE

Ce que le dépôt ne prouve pas

  • Les contrastes sont mesurés sur le CSS généré, pas sur un produit en production.
  • Une seule marque est livrée : le multi-marque est garanti par construction, pas encore par un second projet.
  • Le dépôt ne contient aucun composant d'interface, et n'est pas publié.

Ce qu'il reste à faire

  • Publier le package, après vérification du nom sur npm.
  • Le faire consommer par un package de composants séparé, ce pour quoi il est conçu.