Les design tokens sont la décision structurelle la plus impactante dans tous les design systems sur lesquels j'ai travaillé. Ce n'est pas une tendance ni une fonctionnalité Figma. C'est la couche contractuelle entre le design et le code. Quand c'est bien fait, un changement de couleur dans Figma se propage en production en quelques minutes. Quand c'est mal fait, on passe des semaines à traquer des valeurs hexadécimales codées en dur. Cet article présente l'intégralité du workflow que j'utilise : des Figma Variables aux CSS custom properties en passant par les composants Angular, y compris le theming.
Ce que sont réellement les design tokens
Un design token est une valeur nommée qui représente une décision de design. Au lieu de dire 'ce bouton est #0052CC', on dit 'ce bouton utilise le token de couleur brand-primary.' Le token a un nom, une valeur et une catégorie. Le nom est stable. La valeur peut changer. Cette séparation est ce qui rend le theming et le rebranding réalisables.
Il y a typiquement trois couches de tokens. Les tokens primitifs (aussi appelés globaux ou de référence) sont des valeurs brutes : chaque couleur de la palette, chaque pas d'espacement, chaque taille de police. Les tokens sémantiques (aussi appelés alias ou de décision) référencent les primitifs et portent l'intention de design : color-button-primary-bg, spacing-section-gap, text-heading-large. Les tokens de composant sont les plus spécifiques et référencent les tokens sémantiques : button-primary-background, card-padding.
- Primitif : color-blue-600 = #0052CC
- Sémantique : color-action-primary = color-blue-600
- Composant : button-primary-bg = color-action-primary
Ce modèle à trois couches semble être du surcoût. Ce n'en est pas. C'est ce qui permet de rebrander en changeant un seul mapping de token sémantique au lieu de chercher dans des centaines de composants. Si la couleur d'action principale change, la modification se fait à un seul endroit, le mapping sémantique, et chaque composant qui consomme color-action-primary la récupère sans être touché.
Définir les tokens dans Figma Variables
La fonctionnalité Variables de Figma (sortie en 2023 et significativement améliorée depuis) est maintenant le bon endroit pour définir les design tokens. Avant Variables, les designers utilisaient des plugins comme Tokens Studio. Variables est natif, se synchronise en temps réel, et supporte directement les modes, ce qui est la façon dont le theming fonctionne dans Figma.
Ma structure Figma utilise des collections organisées par couche de token. Je crée trois collections : Primitives, Semantic et Component. Chaque collection peut avoir des modes. La collection Primitives n'a typiquement qu'un seul mode. Les valeurs ne changent pas selon les thèmes. La collection Semantic a des modes pour Light et Dark. La collection Component hérite de Semantic.
- Créer une collection 'Primitives'. Toutes les valeurs brutes de couleur, espacement, rayon, typographie
- Créer une collection 'Semantic' avec les modes Light et Dark. Les valeurs référencent les Primitives
- Créer une collection 'Component'. Les valeurs référencent les tokens Semantic
- Nommer les tokens en kebab-case pour correspondre aux conventions CSS custom property
- Grouper les tokens avec la notation slash : color/action/primary. Figma affiche ça comme une hiérarchie imbriquée
- Documenter chaque token sémantique avec une description : à quoi il sert, pour quoi il ne devrait jamais être utilisé
La discipline de nommage est critique. J'ai vu des systèmes où les tokens sont nommés d'après leurs valeurs (blue-500, 16px) plutôt que leur objectif (action-primary, spacing-base). Quand la valeur change, le nom devient un mensonge. Nommez les tokens pour ce qu'ils signifient, pas ce qu'ils sont actuellement.
Exporter les tokens en CSS Custom Properties
Le pipeline d'export que j'utilise va des Figma Variables à un fichier JSON de tokens puis aux CSS custom properties. Les variables sont exportées de Figma en JSON, le JSON est commité dans le dépôt, et Style Dictionary fait la transformation. L'étape d'export compte moins qu'on ne le pense : n'importe quel plugin ou script qui écrit les variables en JSON fera l'affaire. Ce qui compte, c'est que le JSON atterrisse dans git et que personne ne modifie le CSS généré à la main.
L'étape que la plupart des articles sautent, c'est comment un mode Figma devient un fichier sur le disque. Une variable de la collection Semantic n'a pas une valeur, elle a une valeur par mode, donc un seul export à plat de cette collection est ambigu. Exportez une fois par mode et donnez à chaque export son propre répertoire : tokens/primitives/ pour la collection Primitives, tokens/semantic-light/ et tokens/semantic-dark/ pour les deux modes de Semantic. Les noms de tokens sont identiques dans les deux répertoires de thème et seules les valeurs diffèrent. C'est ce qui permet au build de produire un fichier CSS par thème.
Sur l'outil d'export lui-même, commencez gratuitement. Des plugins communautaires comme variables2json ou Export Variables to JSON vous laissent choisir une collection et un mode et enregistrer le résultat en JSON, ce qui est tout ce dont le pipeline a besoin. L'API REST Variables de Figma est la façon la plus propre d'automatiser le même export, mais elle est réservée au plan Enterprise. N'y passez que si vous êtes déjà sur Enterprise et que vous voulez faire tourner l'export en CI plutôt que dans le navigateur d'un designer.
Style Dictionary est un outil open-source d'Amazon qui transforme les fichiers de tokens dans n'importe quel format de plateforme : CSS custom properties, variables SCSS, constantes Swift iOS, XML Android. Pour les projets web, je le configure pour produire des CSS custom properties organisées par thème. Vérifiez votre version majeure avant de copier une configuration. Tout ce qui suit est en v3 et son API CommonJS. La v4 a changé le point d'entrée en une classe et a retiré token.attributes.category au profit de token.type, donc les filtres v3 ne se transposent pas.
// build-tokens.js (Style Dictionary v3, CommonJS API)
const StyleDictionary = require('style-dictionary');
const cssPlatform = (destination, selector, outputReferences, filter) => ({
css: {
transformGroup: 'css',
prefix: 'ds',
buildPath: 'src/styles/tokens/',
files: [
{ destination, format: 'css/variables', filter, options: { selector, outputReferences } }
]
}
});
// One build per output file, each with its own source glob.
// Do not try to split a single build with filters on token.attributes:
// transformGroup 'css' runs attribute/cti, which overwrites attributes with
// category/type/item derived from the token path. A test like
// attributes.category === 'primitive' never matches and the file builds empty
// with no error.
const isSemantic = (token) => token.filePath.includes('/semantic-');
StyleDictionary.extend({
source: ['tokens/primitives/**/*.json'],
platforms: cssPlatform('primitives.css', ':root', false)
}).buildAllPlatforms();
// include loads the primitives so the var() references resolve.
// isSemantic keeps them out of the theme file.
StyleDictionary.extend({
include: ['tokens/primitives/**/*.json'],
source: ['tokens/semantic-light/**/*.json'],
platforms: cssPlatform('semantic-light.css', ':root, [data-theme="light"]', true, isSemantic)
}).buildAllPlatforms();
StyleDictionary.extend({
include: ['tokens/primitives/**/*.json'],
source: ['tokens/semantic-dark/**/*.json'],
platforms: cssPlatform('semantic-dark.css', '[data-theme="dark"]', true, isSemantic)
}).buildAllPlatforms();L'option outputReferences: true est importante pour les tokens sémantiques. Au lieu de résoudre la valeur hexadécimale finale au moment du build, elle produit var(--ds-color-blue-600), ce qui signifie que le CSS conserve la chaîne de référence. Cela permet le theming à l'exécution sans reconstruire les fichiers de tokens.
Le CSS généré
Après l'exécution de Style Dictionary, on obtient des fichiers CSS qui ressemblent à ça. Les primitifs définissent les valeurs brutes. Les tokens sémantiques référencent les primitifs. Le changement de thème devient un attribut data sur l'élément html.
/* primitives.css */
:root {
--ds-color-blue-100: #EBF2FF;
--ds-color-blue-200: #BDD5FF;
--ds-color-blue-600: #0052CC;
--ds-color-blue-700: #0043A6;
--ds-color-neutral-0: #FFFFFF;
--ds-color-neutral-900: #111827;
--ds-spacing-1: 4px;
--ds-spacing-2: 8px;
--ds-spacing-4: 16px;
--ds-spacing-6: 24px;
--ds-spacing-8: 32px;
--ds-radius-sm: 4px;
--ds-radius-md: 8px;
--ds-radius-lg: 16px;
}
/* semantic-light.css */
:root, [data-theme="light"] {
--ds-color-action-primary: var(--ds-color-blue-600);
--ds-color-action-primary-hover: var(--ds-color-blue-700);
--ds-color-surface-default: var(--ds-color-neutral-0);
--ds-color-text-primary: var(--ds-color-neutral-900);
--ds-spacing-section: var(--ds-spacing-8);
--ds-spacing-component: var(--ds-spacing-4);
}
/* semantic-dark.css */
[data-theme="dark"] {
--ds-color-action-primary: var(--ds-color-blue-200);
--ds-color-action-primary-hover: var(--ds-color-blue-100);
--ds-color-surface-default: var(--ds-color-neutral-900);
--ds-color-text-primary: var(--ds-color-neutral-0);
}Consommer les tokens dans les composants Angular
Les fichiers CSS sont importés une seule fois dans les styles globaux du projet Angular. Les composants individuels n'importent jamais les tokens directement. Ils les consomment via la cascade. C'est le bon pattern. Si un composant importe un fichier de tokens directement, on perd la capacité de surcharger les tokens via les thèmes.
/* styles.scss, global entry point */
@use 'styles/tokens/primitives.css';
@use 'styles/tokens/semantic-light.css';
@use 'styles/tokens/semantic-dark.css';
/* Component styles, no direct token imports */
/* src/app/components/button/button.component.scss */
.btn-primary {
background-color: var(--ds-color-action-primary);
color: var(--ds-color-neutral-0);
padding: var(--ds-spacing-2) var(--ds-spacing-4);
border-radius: var(--ds-radius-md);
border: none;
cursor: pointer;
transition: background-color 200ms ease;
&:hover {
background-color: var(--ds-color-action-primary-hover);
}
&:focus-visible {
outline: 2px solid var(--ds-color-action-primary);
outline-offset: 2px;
}
}Changement de thème à l'exécution
Parce que les tokens sémantiques référencent les primitifs via des chaînes var() que le navigateur résout au moment du calcul de la valeur, le changement de thème nécessite seulement de définir un attribut data-theme sur l'élément document. Pas de re-rendu, pas de gymnastique de toggle de classes, pas de mutations de style JavaScript.
// theme.service.ts
import { Injectable, Renderer2, RendererFactory2 } from '@angular/core';
export type Theme = 'light' | 'dark';
@Injectable({ providedIn: 'root' })
export class ThemeService {
private renderer: Renderer2;
private currentTheme: Theme = 'light';
constructor(rendererFactory: RendererFactory2) {
this.renderer = rendererFactory.createRenderer(null, null);
this.initTheme();
}
private initTheme(): void {
const stored = localStorage.getItem('theme') as Theme;
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const theme = stored ?? (prefersDark ? 'dark' : 'light');
this.setTheme(theme);
}
setTheme(theme: Theme): void {
this.currentTheme = theme;
this.renderer.setAttribute(
document.documentElement,
'data-theme',
theme
);
localStorage.setItem('theme', theme);
}
getTheme(): Theme {
return this.currentTheme;
}
toggle(): void {
this.setTheme(this.currentTheme === 'light' ? 'dark' : 'light');
}
}Surcharges de tokens au niveau du composant
L'un des aspects les plus puissants des CSS custom properties est qu'elles cascadent. On peut les surcharger à n'importe quel scope. Cela permet le theming au niveau du composant sans dupliquer les styles de composant. Une carte inversée, une bannière promotionnelle ou une barre latérale avec un schéma de couleurs différent peut surcharger uniquement les tokens dont elle a besoin.
/* Inverted surface, override tokens at component scope */
.surface-inverted {
--ds-color-action-primary: var(--ds-color-blue-200);
--ds-color-text-primary: var(--ds-color-neutral-0);
--ds-color-surface-default: var(--ds-color-blue-700);
background-color: var(--ds-color-surface-default);
color: var(--ds-color-text-primary);
/* All child components automatically use overridden tokens */
/* No component code changes required */
}Garder Figma et le code synchronisés
Le mode d'échec le plus courant dans les systèmes de design tokens est la dérive. Figma est mis à jour mais pas le code, ou vice versa. J'adresse ça avec une étape de pipeline CI qui exécute Style Dictionary à chaque merge vers main et fait échouer le build si la sortie diffère des fichiers de tokens commités. Cela force un commit délibéré à chaque changement de valeur de token.
- Exporter les tokens depuis Figma selon un calendrier (hebdomadaire ou par release) et commiter le JSON source
- Exécuter Style Dictionary en CI. Faire échouer le build si le CSS généré diffère du CSS commité
- Utiliser le versioning sémantique pour le package de tokens s'il est partagé entre plusieurs applications
- Documenter les changements cassants dans un CHANGELOG. Un renommage de token est un changement cassant pour chaque consommateur
- Ne jamais laisser les développeurs individuels coder en dur des valeurs hexadécimales dans le CSS des composants. Appliquer via le linting
- Envisager une règle Stylelint qui interdit les valeurs de couleur brutes et impose les références var(--ds-)
Le retour sur investissement
L'investissement dans un système de tokens est rentabilisé rapidement. Dans mes derniers mois chez Wonderlabs, en 2023, j'ai mis sur ce pipeline la fondation de design system que j'avais construite pour la PWA de carte de crédit. Ça n'aurait pas pu être plus tôt : Figma n'a livré Variables qu'en juin 2023. Le flux est celui de cet article : des Figma Variables exportées en JSON, Style Dictionary qui transforme ce JSON en CSS custom properties, et des composants Angular qui les consomment par la cascade. Le gain est une propriété de la structure, pas d'une équipe en particulier : les composants bâtis sur des tokens sémantiques sont thémables par défaut, et essayer un ajustement de couleur revient à changer un token et à relancer le build plutôt qu'à modifier les styles des composants.
Les design tokens ne concernent pas l'accord entre Figma et CSS sur une valeur hexadécimale. Ils concernent la création d'un langage partagé entre le design et l'ingénierie qui survit aux changements de personnel, aux rebrandings et à l'évolution des plateformes.
Commencez par les primitifs. Nommez-les pour leur sens, pas leur valeur. Construisez des tokens sémantiques qui capturent l'intention de design. Exportez en CSS custom properties. Laissez la cascade faire le travail de theming. La même structure fonctionne pour une seule application et pour une bibliothèque de composants partagée par plusieurs applications de marques différentes. Seul le nombre de consommateurs change.
Références
- Style Dictionary. https://styledictionary.com/
- Tokens Studio. https://tokens.studio/
- Figma Variables REST API (Enterprise plan only). https://developers.figma.com/docs/rest-api/variables/
- MDN CSS custom properties. https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties
- Design Tokens Community Group (W3C Community Group). Design Tokens Format Module 2025.10. https://www.designtokens.org/tr/2025.10/format/