Skip to main content

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 :

  1. Tokens Primitifs (Global Tokens) : La palette brute. Ils décrivent ce que c'est (ex: blue-500).
  2. Tokens Sémantiques (Alias Tokens) : Le rôle fonctionnel. Ils décrivent à quoi ça sert (ex: action-primary-background).
  3. 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 (ComponentSemanticPrimitive) 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

  1. Catégorie : Le domaine d'application.
    • color : Couleurs.
    • spacing : Marges, paddings.
    • font : Typographie.
    • shadow : Ombres portées.
  2. Type : La fonction sémantique.
    • text : Pour le texte.
    • bg ou surface : Pour les fonds.
    • border : Pour les contours.
    • action : Pour les éléments interactifs.
  3. Item : La hiérarchie ou le rôle.
    • primary, secondary, tertiary.
    • success, warning, error, info.
    • subtle, strong.
  4. État / Variante : La modification contextuelle.
    • hover, active, disabled, focused.

Exemple concret : --color-bg-action-primary-hover

  • color → Catégorie
  • bg → 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.

  1. Définissez 3 tokens primitifs pour le rouge, l'orange et le vert.
  2. Créez 3 tokens sémantiques : --color-status-error, --color-status-warning, --color-status-success.
  3. Appliquez-les à une classe .alert via 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 PrimitifSé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émantiqueMode 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 :

  1. Définissez les tokens primitifs correspondant aux valeurs du tableau (palette brute : gris, bleu, jaune, noir, blanc).
  2. Définissez les tokens sémantiques pour le mode clair dans :root.
  3. Créez [data-theme="dark"] et [data-theme="high-contrast"] pour redéfinir uniquement les tokens sémantiques.
  4. Appliquez les tokens au composant .product-card et à 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;
}