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

// 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

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;
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.

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.

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.

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;
}| Desktop | Mobile, 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.