Du token Figma au composant en production
La promesse d'un design system n'est pas d'avoir de beaux composants dans Figma. C'est que ces composants arrivent en production avec exactement les mêmes valeurs, dans React, dans Angular, en light et en dark. Voici le pipeline concret pour y arriver sans bricoler.

Le token est la vraie source de vérité
Dans la plupart des projets, la source de vérité du design est, en pratique, une capture d'écran et la bonne volonté du développeur qui inspecte Figma. C'est fragile par construction : deux personnes regardent le même fichier et en sortent deux valeurs légèrement différentes, et personne ne s'en aperçoit avant la recette.
Les design tokens cassent ce modèle. Un token n'est pas une valeur, c'est un nom qui pointe vers une valeur. --ds-color-action-background n'est pas #2D7D9A figé dans le marbre : c'est un contrat nommé, résolu à l'exécution selon le contexte. Quand le contexte est "dark mode", la même variable pointe vers une valeur différente, sans que le composant en sache rien. Cette indirection est le cœur de tout le système.
Ce qui change concrètement : dès qu'une décision de design est encodée dans un token, elle n'appartient plus à un fichier Figma ou à un fichier CSS. Elle appartient au système. Toute modification se propage partout, automatiquement, sans réunion de coordination.
La chaîne : variable Figma, export, CSS custom properties
Figma Variables (depuis 2023) est le point d'entrée naturel pour les équipes design-code. On définit les tokens directement dans Figma, couleurs, spacing, radius, typographie, et on les exporte via un script ou un plugin vers un fichier JSON structuré.
Ce JSON alimente ensuite un outil de transformation (Style Dictionary est la référence, W3C Design Tokens est le format standard émergent) qui génère les cibles souhaitées :
- Un fichier
tokens.cssavec des CSS custom properties (--ds-spacing-3: 16px,--ds-radius-md: 8px) - Un fichier JS/TS qui exporte les mêmes valeurs sous forme de constantes pour les outils qui les consomment côté JavaScript
- Optionnellement : un preset Tailwind si l'équipe l'utilise côté web (
theme.extend.colorsalimenté par le même JSON)
Le fichier CSS généré est publié dans un package npm partagé, dans notre cas @acme/theming. Toutes les apps l'importent. C'est la seule source de vérité côté code. On n'écrit jamais de valeur brute dans un composant.
Un token n'est pas une couleur. C'est un nom qui engage le système entier à tenir une promesse.
Le dernier kilomètre : React 19 et Angular 21 depuis le même contrat
C'est là que les équipes perdent du temps : le CSS est commun, mais les composants divergent. React fait une chose, Angular en fait une autre légèrement différente, et six mois plus tard on a deux systèmes parallèles qui partent chacun dans leur direction.
La règle que j'applique est simple : un seul fichier CSS, partagé à l'identique entre les deux packages. Les classes BEM (.ds-button, .ds-button--primary, .ds-button--sm) sont le contrat visuel. React et Angular doivent produire exactement les mêmes classes dans le DOM.
Côté React 19, on utilise forwardRef avec asChild via Radix Slot pour la flexibilité du host element, et des record maps statiques pour traduire les props en classes sans logique conditionnelle éparpillée :
const APPEARANCE_CLASS: Record<ButtonAppearance, string> = {
filled: "ds-button--filled",
outlined: "ds-button--outlined",
ghost: "",
};
Côté Angular 21, on ne crée pas de wrapper <div>. On utilise un attribute selector : button[arvButton], a[arvButton]. Le composant Angular se "pose" sur l'élément natif existant. C'est fondamental pour l'accessibilité : un bouton reste un <button> dans le DOM, avec son rôle ARIA implicite, sans couche supplémentaire. Les classes sont calculées via un computed() Signal et appliquées via le host binding [class].
Le naming sémantique, ou comment ne pas tout casser au premier theming
La question qu'on ne pose pas assez tôt : à quel niveau d'abstraction nommer les tokens ? Il y a trois niveaux, et les confondre est la source de la plupart des bugs de theming.
- Primitif : la valeur brute.
brand-600: #2D7D9A. Ne jamais exposer côté composant. - Sémantique : le rôle.
action/backgroundpointe versbrand-600en light, versgreen-400en dark. C'est ce niveau que les composants consomment. - Composant : optionnel, pour les valeurs très spécifiques à un seul composant.
button/padding-x. Utile pour les overrides locaux sans toucher au global.
L'erreur classique : exposer le token primitif directement dans le composant (var(--ds-brand-600) au lieu de var(--ds-color-action-background)). Le composant fonctionne en light. Il casse immédiatement en dark parce que le primitif ne change pas de valeur selon le thème, seul le token sémantique le fait.
Une fois ce principe en place, ajouter un nouveau thème de marque (la marque B, une autre filiale) revient à définir les résolutions sémantiques dans une nouvelle couche CSS. Les composants ne bougent pas.
Automatiser la synchronisation : CI et détection de drift
Le plus beau pipeline manuel reste fragile. Un designer modifie un token dans Figma un jeudi soir, personne n'exporte, et le code continue à vivre avec l'ancienne valeur pendant trois semaines. Ce n'est pas un problème de discipline, c'est un problème d'automatisation absente.
Ce qu'on met en place :
- Un export automatique déclenché depuis Figma (webhook ou action GitHub programmée) qui génère le JSON et ouvre une PR si des valeurs ont changé
- Un snapshot test dans la CI qui compare les tokens générés au fichier de référence, toute divergence bloque le merge
- Une vérification de build dans les deux packages React et Angular :
npm run builddoit passer et lestyles.cssgénéré doit inclure les nouvelles classes
Pour les composants React, on lance Vitest avec React Testing Library et axe-core à chaque commit. Un test axe propre (results.violations.toEqual([])) est la preuve que l'accessibilité n'a pas régressé. Pour Angular, ng-packagr via la CI est la vérification de build, le package doit compiler en FESM2022 avec les types générés correctement.
Les trois pièges qui font échouer les bonnes intentions
Après avoir vu plusieurs pipelines partir dans le décor, voici les pièges qui reviennent le plus souvent :
- Les valeurs en dur dans les composants.
style={{ color: "#2D7D9A" }}dans un composant React signifie que cette valeur ne répondra jamais au dark mode, à un rebranding ou à un override. La règle : zéro valeur brute dans un composant. Uniquement desvar(--ds-*). - Les tokens primitifs exposés. Quand l'équipe explose les primitives dans le CSS partagé (
--ds-brand-600,--ds-neutral-900), les développeurs les utilisent parce qu'ils sont disponibles. Et ça paraît marcher. Jusqu'au premier thème alternatif où tout déraille. La solution : publier uniquement les tokens sémantiques dans le package partagé. Les primitifs restent internes à la pipeline de génération. - Le dark mode ajouté après coup. Dark mode en retro-fit est toujours douloureux. Si les tokens sémantiques existent dès le début et que les composants ne consomment que des sémantiques, le dark mode est une couche CSS additionnelle, pas une refonte. La règle : penser dual-mode dès le premier token.
En conclusion
La force d'un design system ne se voit pas dans Figma. Elle se voit dans la capacité de l'équipe à modifier un token à 15h et à voir le changement déployé sans bug à 16h, dans React, dans Angular, en light et en dark. C'est ce pipeline de bout en bout qui transforme un kit de composants en infrastructure design réelle.
Le travail n'est pas glamour : nommer correctement les tokens sémantiques, configurer Style Dictionary, synchroniser deux packages, écrire des tests axe. Mais c'est ce socle qui libère ensuite toute la vélocité, y compris quand on branche de l'IA générative sur le système pour générer des écrans. Une IA branchée sur un design system propre produit quelque chose d'utilisable. Une IA branchée sur du CSS éparpillé produit du bruit.