Chapitre 36 : Design Tokens : structuration et nomenclature
Organisation des valeurs sémantiques vs primitives pour une scalabilité du design system.
L'Architecture des Design Tokens
1. Quoi
Les Design Tokens sont des entités atomiques qui stockent des décisions de design (couleurs, espacements, typographies, ombres) sous forme de variables. Contrairement à une simple variable CSS, un token ne définit pas seulement une valeur, mais lui attribue un sens et une intention.
L'architecture moderne des tokens repose sur une structure à trois niveaux :
- Tokens Primitifs (Global Tokens) : La palette brute. Ils décrivent ce que c'est (ex:
blue-500). - Tokens Sémantiques (Alias Tokens) : Le rôle fonctionnel. Ils décrivent à quoi ça sert (ex:
action-primary-background). - Tokens de Composants (Component Tokens) : La spécificité extrême. Ils décrivent où c'est utilisé (ex:
button-submit-bg).
2. Pourquoi
Dans un projet professionnel, utiliser uniquement des variables primitives (ex: --color-blue-500) crée une dépendance rigide. Si vous décidez que le bouton principal doit devenir violet, vous devrez parcourir tout votre CSS pour remplacer blue-500 par purple-500, au risque d'en modifier d'autres éléments qui étaient bleus pour d'autres raisons.
En introduisant une couche sémantique, vous créez une abstraction. Vous changez la valeur du token sémantique action-primary pour pointer vers purple-500, et l'ensemble de l'interface se met à jour sans ambiguïté. C'est le fondement indispensable pour :
- Le Theming (Mode sombre/clair).
- La Maintenance à grande échelle.
- La Synchronisation entre designers (Figma) et développeurs.
3. Comment
A. Syntaxe de base (L'approche naïve)
L'approche naïve consiste à déclarer des variables et à les utiliser directement.
:root {
/* Primitif utilisé directement (Risqué) */
--color-blue-500: #3b82f6;
}
.btn {
background-color: var(--color-blue-500);
}
B. Cas concret : Architecture robuste
Voici comment structurer un système de tokens professionnel en utilisant les Custom Properties CSS.
:root {
/* ==========================================================================
1. TOKENS PRIMITIFS (The Palette)
On ne les utilise JAMAIS directement dans les composants.
========================================================================== */
--primitive-blue-100: #dbeafe;
--primitive-blue-500: #3b82f6;
--primitive-blue-900: #1e3a8a;
--primitive-gray-100: #f3f4f6;
--primitive-gray-900: #111827;
--primitive-spacing-2: 0.5rem;
--primitive-spacing-4: 1rem;
/* ==========================================================================
2. TOKENS SÉMANTIQUES (The Meaning)
Ils font le pont entre la palette et l'usage.
========================================================================== */
--color-text-main: var(--primitive-gray-900);
--color-text-muted: var(--primitive-gray-500);
--color-bg-canvas: var(--primitive-gray-100);
--color-action-primary: var(--primitive-blue-500);
--color-action-primary-hover: var(--primitive-blue-900);
--spacing-container: var(--primitive-spacing-4);
/* ==========================================================================
3. TOKENS DE COMPOSANTS (The Specificity)
Optionnel, utile pour les composants complexes.
========================================================================== */
--btn-primary-bg: var(--color-action-primary);
--btn-primary-text: white;
}
/* Application dans le CSS */
.button-primary {
background-color: var(--btn-primary-bg);
color: var(--btn-primary-text);
padding: var(--primitive-spacing-2) var(--spacing-container);
border: none;
border-radius: 4px;
transition: background 0.2s ease;
}
.button-primary:hover {
background-color: var(--color-action-primary-hover);
}
C. Limitations
- Performance : Une chaîne de variables trop longue (
Component→Semantic→Primitive) peut théoriquement impacter le rendu, bien que ce soit négligeable sur les navigateurs modernes. - Complexité cognitive : Pour un petit projet, trois couches de tokens sont surdimensionnées et ralentissent le développement.
- Outillage : Gérer des centaines de tokens à la main dans un fichier CSS devient vite ingérable. On utilise alors des outils comme Style Dictionary pour générer ces fichiers depuis un JSON.
4. Zone de Danger
❌ Erreur commune : Nommer par la valeur
--color-light-blue: #add8e6;
Si le design change et que le bleu devient foncé, le nom --color-light-blue devient un mensonge technique.
✅ Bonne pratique : Nommer par l'intention
--color-info-surface: var(--primitive-blue-100);
Peu importe la couleur finale, le token décrit toujours une "surface d'information".
Hiérarchie des Design Tokens
Nomenclature et Convention de nommage
Pour maintenir la cohérence, une nomenclature stricte est nécessaire. Le pattern le plus répandu est le suivant :
[Catégorie]-[Type]-[Item]-[État/Variante]
Détail des segments
- Catégorie : Le domaine d'application.
color: Couleurs.spacing: Marges, paddings.font: Typographie.shadow: Ombres portées.
- Type : La fonction sémantique.
text: Pour le texte.bgousurface: Pour les fonds.border: Pour les contours.action: Pour les éléments interactifs.
- Item : La hiérarchie ou le rôle.
primary,secondary,tertiary.success,warning,error,info.subtle,strong.
- État / Variante : La modification contextuelle.
hover,active,disabled,focused.
Exemple concret :
--color-bg-action-primary-hover
color→ Catégoriebg→ Type (fond)action-primary→ Item (action principale)hover→ État
Questions clés
1. Quelle est la différence fondamentale entre une variable CSS classique et un Design Token ?
Découvrir la réponse
Une variable CSS est un outil technique pour stocker une valeur. Un Design Token est une décision de design nommée sémantiquement. La variable est le moyen, le token est la méthodologie.
2. Pourquoi ne faut-il jamais utiliser de tokens primitifs directement dans les composants ?
Découvrir la réponse
Pour éviter le couplage fort. Si vous utilisez --primitive-blue-500 partout et que vous voulez changer le bleu du thème, vous devrez modifier chaque occurrence. Avec un token sémantique --color-brand, vous ne modifiez qu'une seule ligne.
3. Comment gérer le mode sombre avec cette structure ?
Découvrir la réponse
On ne change pas les tokens primitifs (le bleu reste le bleu), on redéfinit les tokens sémantiques dans une classe ou un attribut.
:root { --color-bg-canvas: var(--primitive-gray-100); }
[data-theme="dark"] { --color-bg-canvas: var(--primitive-gray-900); }
4. Est-il nécessaire de créer des tokens de composants pour chaque élément ?
Découvrir la réponse
Non. C'est une approche "opt-in". On les crée uniquement pour des composants complexes dont le style doit être modulable sans impacter le reste du système sémantique.
Mise en pratique
Exercice 1 : Reproduction guidée
Objectif : Créer une structure minimale de tokens pour un système d'alertes.
- Définissez 3 tokens primitifs pour le rouge, l'orange et le vert.
- Créez 3 tokens sémantiques :
--color-status-error,--color-status-warning,--color-status-success. - Appliquez-les à une classe
.alertvia une variable de composant--alert-bg.
Découvrir la solution commentée
:root {
/* Primitifs */
--primitive-red-500: #ef4444;
--primitive-orange-500: #f97316;
--primitive-green-500: #22c55e;
/* Sémantiques */
--color-status-error: var(--primitive-red-500);
--color-status-warning: var(--primitive-orange-500);
--color-status-success: var(--primitive-green-500);
}
/* Composant */
.alert {
/* On utilise une variable locale pour permettre l'override */
--alert-bg: var(--color-status-success);
background-color: var(--alert-bg);
padding: 1rem;
border-radius: 8px;
}
.alert-error { --alert-bg: var(--color-status-error); }
.alert-warning { --alert-bg: var(--color-status-warning); }
Exercice 2 : Adaptation
Objectif : Transformer un CSS "hardcodé" en système de tokens.
Voici le code source :
.card {
background: #ffffff;
border: 1px solid #e5e7eb;
padding: 16px;
color: #111827;
}
.card:hover {
border-color: #3b82f6;
}
Consigne : Réécrivez ce code en utilisant la hiérarchie Primitif → Sémantique.
Découvrir la solution commentée
:root {
/* Primitifs */
--primitive-white: #ffffff;
--primitive-gray-200: #e5e7eb;
--primitive-gray-900: #111827;
--primitive-blue-500: #3b82f6;
--primitive-spacing-md: 16px;
/* Sémantiques */
--color-surface-main: var(--primitive-white);
--color-border-subtle: var(--primitive-gray-200);
--color-border-active: var(--primitive-blue-500);
--color-text-main: var(--primitive-gray-900);
--spacing-card-padding: var(--primitive-spacing-md);
}
.card {
background: var(--color-surface-main);
border: 1px solid var(--color-border-subtle);
padding: var(--spacing-card-padding);
color: var(--color-text-main);
}
.card:hover {
border-color: var(--color-border-active);
}
Exercice 3 : Conception
Objectif : Concevoir un système de tokens pour un site e-commerce supportant un "Mode Nuit" et un "Mode Contraste Élevé".
HTML de départ :
<body data-theme="light"> <!-- Changez en "dark" ou "high-contrast" pour tester -->
<div class="product-card">
<h3 class="product-title">Casque Audio Pro</h3>
<p class="product-description">Son exceptionnel avec réduction de bruit active.</p>
<span class="product-price">129,99 €</span>
<button class="btn-add">Ajouter au panier</button>
</div>
</body>
Tokens sémantiques à définir et valeurs attendues :
| Token sémantique | Mode clair (:root) | Mode nuit (dark) | Mode contraste élevé (high-contrast) |
|---|---|---|---|
--color-bg-canvas | #f3f4f6 | #111827 | #000000 |
--color-bg-surface | #ffffff | #1f2937 | #000000 |
--color-text-main | #111827 | #f9fafb | #ffffff |
--color-text-muted | #6b7280 | #9ca3af | #ffffff |
--color-border-subtle | #e5e7eb | #374151 | #ffffff |
--color-action-primary | #3b82f6 | #60a5fa | #ffff00 |
--color-on-action | #ffffff | #ffffff | #000000 |
Typographie attendue :
.product-title:font-size: 1.125rem,font-weight: 600.product-description:font-size: 0.875rem.product-price:font-size: 1.25rem,font-weight: 700, affiché en bloc
Structure et bordures de la carte :
padding: 1.5rem,border-radius: 8px,display: flex; flex-direction: column; gap: 0.75rem- Mode clair / nuit : bordure
1px solid var(--color-border-subtle) - Mode contraste élevé : épaissir la bordure à
3px
Consignes :
- Définissez les tokens primitifs correspondant aux valeurs du tableau (palette brute : gris, bleu, jaune, noir, blanc).
- Définissez les tokens sémantiques pour le mode clair dans
:root. - Créez
[data-theme="dark"]et[data-theme="high-contrast"]pour redéfinir uniquement les tokens sémantiques. - Appliquez les tokens au composant
.product-cardet à ses éléments enfants.
Découvrir la solution commentée
:root {
/* ===== PRIMITIFS ===== */
--primitive-white: #ffffff;
--primitive-black: #000000;
--primitive-yellow-300: #ffff00;
--primitive-gray-50: #f9fafb;
--primitive-gray-100: #f3f4f6;
--primitive-gray-200: #e5e7eb;
--primitive-gray-500: #6b7280;
--primitive-gray-400: #9ca3af;
--primitive-gray-700: #374151;
--primitive-gray-800: #1f2937;
--primitive-gray-900: #111827;
--primitive-blue-400: #60a5fa;
--primitive-blue-500: #3b82f6;
/* ===== SÉMANTIQUES — Mode Clair (par défaut) ===== */
--color-bg-canvas: var(--primitive-gray-100);
--color-bg-surface: var(--primitive-white);
--color-text-main: var(--primitive-gray-900);
--color-text-muted: var(--primitive-gray-500);
--color-border-subtle: var(--primitive-gray-200);
--color-border-width: 1px;
--color-action-primary: var(--primitive-blue-500);
--color-on-action: var(--primitive-white);
}
/* ===== MODE NUIT ===== */
[data-theme="dark"] {
--color-bg-canvas: var(--primitive-gray-900);
--color-bg-surface: var(--primitive-gray-800);
--color-text-main: var(--primitive-gray-50);
--color-text-muted: var(--primitive-gray-400);
--color-border-subtle: var(--primitive-gray-700);
/* --color-border-width inchangé : 1px */
--color-action-primary: var(--primitive-blue-400); /* Bleu plus clair sur fond sombre */
--color-on-action: var(--primitive-white);
}
/* ===== MODE CONTRASTE ÉLEVÉ ===== */
[data-theme="high-contrast"] {
--color-bg-canvas: var(--primitive-black);
--color-bg-surface: var(--primitive-black);
--color-text-main: var(--primitive-white);
--color-text-muted: var(--primitive-white); /* Pas de subtilité : tout doit être lisible */
--color-border-subtle: var(--primitive-white);
--color-border-width: 3px; /* Bordure épaisse pour maximiser la visibilité */
--color-action-primary: var(--primitive-yellow-300); /* Jaune vif sur noir = contraste maximal */
--color-on-action: var(--primitive-black);
}
/* ===== APPLICATION AUX COMPOSANTS ===== */
body {
background-color: var(--color-bg-canvas);
color: var(--color-text-main);
font-family: sans-serif;
padding: 2rem;
transition: background-color 0.3s ease, color 0.3s ease;
}
.product-card {
background-color: var(--color-bg-surface);
border: var(--color-border-width) solid var(--color-border-subtle);
border-radius: 8px;
padding: 1.5rem;
display: flex;
flex-direction: column;
gap: 0.75rem;
max-width: 320px;
transition: border-color 0.3s ease;
}
.product-title {
font-size: 1.125rem;
font-weight: 600;
color: var(--color-text-main);
}
.product-description {
font-size: 0.875rem;
color: var(--color-text-muted);
}
.product-price {
display: block;
font-size: 1.25rem;
font-weight: 700;
color: var(--color-action-primary);
}
.btn-add {
background-color: var(--color-action-primary);
color: var(--color-on-action);
border: none;
border-radius: 4px;
padding: 0.5rem 1rem;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
align-self: flex-start;
transition: opacity 0.2s ease;
}
.btn-add:hover {
opacity: 0.85;
}