Si tu bosses sur un produit qui vit plus de 6 mois, tu l’as déjà vu. Le design system “dans le code” avance (composants, variantes, props, refactors). Le design system “dans Figma” devient un screenshot un peu honteux de l’an dernier. Et tout le monde finit par dire la même phrase, avec un soupir : « la parité Figma ↔ code est cassée ».
Mon avis est simple : ce n’est pas un problème d’outil. C’est un problème de contrat. Personne ne possède une spec minimale qui dit clairement ce qui existe, comment ça s’appelle, ce que ça veut dire, et comment ça évolue. Du coup, la dérive n’est pas un accident. C’est la trajectoire normale du système.
Les symptômes : quand tu “as” un design system, mais pas le même selon l’onglet
La dérive (“drift”) se voit très vite si tu arrêtes de regarder les jolies frames et que tu regardes le quotidien. Côté code, tu as un composant Button avec 12 variantes parce que le produit a de vrais besoins. Côté Figma, tu as 4 variantes parce que personne n’a eu le temps de reprendre le fichier. Ou l’inverse, d’ailleurs : une librairie Figma hyper riche, mais une implémentation code qui a contourné la moitié des comportements parce que la deadline.
Le pire, ce n’est même pas l’écart fonctionnel. Le pire, c’est l’écart de langage. Les noms dérivent. Une variante “Primary / Strong” devient “Brand / Solid” dans le code. Les tokens changent de sémantique au fil des tickets (“on mettra un bleu un peu plus… premium”), puis personne n’ose toucher le nom parce que ça casserait des usages. Résultat, tu te retrouves avec des tokens qui mentent et des composants qui ont des props “historiques”. Et là, la parité est morte, parce que même si tu recopies tout à la main, tu recopies des concepts différents.
Dernier symptôme très produit long-courrier : Figma est vécu comme un snapshot. On y va pour valider l’intention, pas pour constater la réalité. Le code devient la vérité de facto, et Figma devient un support de présentation. À partir de là, demander “la parité” est presque injuste. On compare deux choses qui n’ont plus le même rôle.
Sponsorisé par Le Scribouillard
Besoin de contenu optimisé SEO ?
Utilisez la meilleure plateforme française de création de contenu assistée par IA ! Et générez des articles pour moins de 1€ !
Le vrai bug : sans « contrat », tu n’as pas de source de vérité, juste des opinions
Un design system n’est pas “une librairie de composants”. C’est un ensemble de décisions partagées. Et une décision partagée, pour tenir dans le temps, a besoin d’un support qui survit aux personnes. C’est ça que j’appelle le contrat.
Le contrat, ce n’est pas un doc de 40 pages. C’est une spec minimale, versionnée, qui donne une réponse claire à des questions très concrètes. Quels tokens existent et quelle est leur sémantique (pas leur valeur du jour). Quels composants existent. Quels états sont obligatoires (disabled, loading, focus-visible, error…). Quelles variantes sont “produit” et lesquelles sont des hacks temporaires. Et surtout, comment on nomme les choses pour que les designers, les devs et la QA parlent du même objet.
Et il y a une question qui pique, mais qu’il faut poser noir sur blanc : qui possède ce contrat ? Si la réponse est “tout le monde”, en pratique c’est “personne”. Et si c’est “l’équipe design”, mais que le code shippe sans gate, alors le contrat n’est pas un contrat. C’est un souhait.
Ce que j’entends par « contrat » : une spec versionnée, pas un fichier Figma
Pour moi, le contrat doit être un artefact qui a les propriétés suivantes : lisible, diffable, versionné, et utilisable par les deux mondes. Donc, oui, un repo. Oui, des PR. Oui, un changelog. Parce que si tu ne peux pas faire un diff et dire « ça a changé ici », tu ne peux pas détecter la dérive, seulement la ressentir.
Très concrètement, la spec minimale du contrat contient deux choses. D’abord des tokens (couleurs, espaces, radius, typographies, elevations…) avec une sémantique stable. Ensuite une couche components qui décrit l’API de tes composants (props, variantes, états), pas leur implémentation. Le contrat ne doit pas “décrire React” ou “décrire Figma”. Il doit décrire le système.
Ce point est important : tant que tu fais de Figma la source de vérité “par défaut”, tu te tires une balle sur deux sujets. Le premier, c’est la vérifiabilité. Le second, c’est que Figma est très bon pour représenter un état, moins bon pour représenter une API vivante (des combinaisons d’états, des contraintes, des comportements). À l’inverse, tant que tu fais du code la source de vérité “par défaut”, tu condamnes le design à courir derrière la release, et tu perds le bénéfice d’un système partagé en amont.
Pourquoi le Design Tokens Format (DTCG) aide : tu arrêtes d’inventer un JSON maison
La tentation classique, quand on veut “un contrat”, c’est de créer un format de tokens interne. Une sorte de JSON artisanal qui marche très bien… jusqu’au jour où tu veux le connecter à un plugin Figma, un build CSS, une lib mobile, un autre repo, ou juste recruter quelqu’un qui ne connaît pas ton dialecte.
Le Design Tokens Format (draft du Design Tokens Community Group) apporte surtout un truc précieux : une structure et des mécanismes partagés pour exprimer des tokens, leurs types, et leurs relations. Notamment les références (un token qui pointe vers un autre) et des notions d’extension / héritage. Ce n’est pas “la solution magique”, mais c’est un moyen d’arrêter de bricoler la grammaire de base et de concentrer ton énergie sur ce qui compte vraiment : la sémantique et la gouvernance.
Exemple volontairement simple. Tu veux un token sémantique de couleur de texte, qui référence un token de palette. Tu veux pouvoir lire ça facilement, le diff facilement, et le transformer vers CSS variables, Android, iOS, etc.
{
"$schema": "https://www.designtokens.org/schemas/draft/format.json",
"tokens": {
"color": {
"blue": {
"600": { "type": "color", "value": "#2563EB" }
},
"text": {
"default": { "type": "color", "value": "{tokens.color.blue.600}" }
}
}
}
}
Le détail qui change la vie, ce n’est pas le JSON. C’est l’idée que tu standardises la mécanique de référence, donc tu peux outiller derrière. Et quand tu peux outiller, tu peux contrôler. Et quand tu peux contrôler, tu peux tenir la parité dans le temps.
Le workflow qui tient : PR tokens + changelog + pages de référence + diff automatique
La plupart des équipes échouent parce qu’elles veulent un “sync” permanent entre Figma et le code. C’est une fausse bonne idée. Le vrai objectif, c’est d’avoir une chaîne de changement fiable, avec un endroit où ça discute, un endroit où ça se valide, et un endroit où ça se publie.
Le workflow que je vois marcher est boring, mais solide. Toute évolution de tokens passe par une PR dans un repo de design system. La PR contient le changement, un bout de changelog (même court), et idéalement une mise à jour de pages de référence générées (doc site, Storybook, ou un site interne). Ensuite, à la CI, tu génères un artefact diffable. Typiquement un export CSS variables, un export JSON “plat”, et une page de preview qui montre “avant/après” pour les couleurs, l’échelle d’espacement, les typos. Tu veux que la review puisse se faire sans ouvrir Figma et sans lancer l’app.
Ce qui est important, c’est que la discussion se fasse au bon endroit. Dans la PR, tu peux dire « non, ce token n’est pas sémantique », ou « ce naming va nous coûter cher », ou « cette variante de composant n’a pas de raison d’être dans le DS ». Si tu laisses la discussion se faire dans un commentaire Figma d’un fichier qui sera dupliqué 15 fois, tu ne la retrouveras jamais. Et tu ne sauras jamais pourquoi on a pris cette décision.
Qui a le droit de créer un token (et pourquoi dire “non” est une compétence)
Le drift vient rarement d’une mauvaise intention. Il vient d’un truc très humain : face à un besoin produit urgent, la solution la plus rapide est de créer “un nouveau token” ou “une nouvelle variante”. Et si personne ne se sent légitime pour dire non, tu accumules. Au bout de quelques mois, ton système ressemble à un tiroir de câbles.
Je suis assez dur là-dessus : créer un token, ce n’est pas “ajouter une valeur”. C’est créer un engagement. Tu engages le design, le code, la doc, les usages, et les migrations futures. Donc oui, tout le monde doit pouvoir proposer. Mais non, tout le monde ne doit pas pouvoir merger. Il te faut un ownership explicite, avec une petite poignée de personnes (design + dev) qui protègent le contrat.
Et surtout, il te faut des raisons acceptables de dire non. “Non, ça c’est une exception produit, on la garde localement.” “Non, on ne met pas un token pour ce bleu précis, on veut un token sémantique.” “Non, on ne renomme pas 40 trucs parce que ça ‘sonne mieux’, on n’a pas le budget migration.” Ce n’est pas de la rigidité. C’est de la survie.
Naming : le piège discret qui flingue la parité plus vite que les pixels
Quand Figma et le code divergent, on parle souvent de styles, de variables, de composants. Mais le poison lent, c’est le naming. Parce qu’un naming divergent se propage partout. Dans les tickets, dans les props, dans la doc, dans les tests, dans les conversations. Et après ça, même quand tu “réalignes” les libs, les gens continuent d’utiliser les anciens mots. Tu as officiellement la parité, mais officieusement tu as deux langues.
Ma règle terrain : sépare clairement palette et sémantique. Une palette du type blue/600 peut vivre. Mais un token utilisé par les produits devrait être sémantique et stable, du type color.text.default, color.bg.surface, color.border.muted. Parce que c’est ça que tu peux faire évoluer sans casser le monde. Tu changes la valeur, pas le contrat.
Et si tu n’as pas l’énergie pour une refonte de naming complète, fais au moins un truc : arrête d’ajouter des nouveaux noms “à l’arrache”. Chaque nouveau token doit respecter la même logique. Le backlog de dette existe, ok. Mais n’empile pas la dette en temps réel.
Détecter le drift avant qu’il coûte 2 sprints : des checks simples, mais automatiques
Le drift est inévitable. Ce qui ne doit pas être inévitable, c’est de le découvrir après coup, quand trois équipes ont déjà forké des solutions.
Tu n’as pas besoin d’un système de monitoring digne de la NASA. Tu as besoin de deux ou trois contrôles automatiques qui tournent à chaque PR. Un exemple très concret : vérifier que tous les tokens sémantiques utilisés dans le code existent dans le repo de tokens. Ou l’inverse, vérifier que des tokens “publics” ne sont plus utilisés nulle part (ça sent le token zombie). Tu peux commencer bête, en cherchant des usages de var(--...) ou des noms de tokens dans tes styles, et en comparant à une liste générée.
// Exemple naïf mais utile : vérifier que les CSS vars utilisées existent.
import fs from 'node:fs';
const css = fs.readFileSync('dist/theme.css', 'utf8');
const declared = new Set([...css.matchAll(/--([a-z0-9-]+)\s*:/g)].map(m => m[1]));
const appCss = fs.readFileSync('src/app.css', 'utf8');
const used = new Set([...appCss.matchAll(/var\(--([a-z0-9-]+)\)/g)].map(m => m[1]));
const missing = [...used].filter(x => !declared.has(x));
if (missing.length) {
console.error('Missing tokens:', missing.slice(0, 20));
process.exit(1);
}
Ce script ne remplace pas une vraie stratégie, mais il fait un truc essentiel : il rend le drift visible au moment où tu peux encore corriger sans douleur. Et c’est ça le but. La parité ne se “maintient” pas avec de la bonne volonté. Elle se maintient avec des garde-fous.
Les mauvais arbitrages que je vois tout le temps
Le premier, c’est vouloir absolument un “round-trip” parfait entre Figma et le code. Genre “on push depuis Figma vers Git et on pull depuis Git vers Figma”. Ça peut marcher dans certaines orgs très matures, avec un outillage très propre. Mais dans la plupart des équipes, ça devient vite une usine à gaz, donc plus personne ne la respecte, donc tu as quand même du drift. En pire.
Le deuxième, c’est de traiter les tokens comme “un export technique”. Un fichier généré, qu’on ne review pas, qu’on ne discute pas. Mauvaise direction. Les tokens sont une interface publique. Si tu ne review pas ton interface publique, tu vas la regretter.
Le troisième, c’est de créer des composants “fantômes”. Un composant existe dans le code, mais n’existe pas comme concept dans le DS. Ou l’inverse. Ça arrive quand tu n’as pas de contrat côté composants. Et après, tu te bats sur des débats absurdes du type « mais ce composant, c’est juste un wrapper ». Peut-être. Mais s’il est utilisé partout, il a un coût. Donc il doit être nommé, décrit, et possédé.
Mon modèle mental : Figma et le code ne doivent pas être “égaux”, ils doivent être raccord
La parité au sens strict, c’est souvent un fantasme. Figma est fort pour représenter des intentions, des règles visuelles, des patterns. Le code est fort pour représenter des comportements, des contraintes, des API, des interactions réelles. Vouloir les rendre identiques pixel par pixel et concept par concept peut te faire perdre ton temps.
Ce que tu veux, c’est qu’ils soient raccord. Même vocabulaire. Même sémantique. Même ensemble de décisions. Et quand ça change, tu veux un endroit où le changement est explicite, versionné, reviewé. Bref, un contrat possédé.
Conclusion : la parité n’est pas un objectif, c’est un effet secondaire d’une bonne gouvernance
Si tu ne retiens qu’une idée, garde celle-là : la parité Figma ↔ code ne se répare pas avec “un effort de sync”. Elle se répare en posant un contrat minimal, en le versionnant, et en donnant à quelqu’un le pouvoir de le défendre. Ensuite seulement tu choisis les outils, les exports, les plugins, les automatisations.
Et le truc cool, c’est que ça scale mieux que tu ne penses. Quand l’équipe change, quand le produit s’étend, quand tu ajoutes une plateforme, tu n’as pas à réexpliquer toute l’histoire. Tu as un endroit où la vérité vit, et un processus pour la faire évoluer proprement. C’est moins sexy que “un plugin magique”. Mais c’est ce qui fait tenir un design system pendant des années.