Aller au contenu principal
Design & Contenu11 février 20269 min

Design tokens et accessibilité : intégrer les contrastes dans votre design system

En Bref : L'essentiel à retenir

  • Les design tokens sont des variables centralisées (couleurs, tailles, espacements) qui alimentent un design system. Mal structurés, ils propagent des problèmes de contraste à l'échelle de tout le produit.
  • Organiser les tokens en couches (primitives, sémantiques, composants) permet d'encoder les contraintes WCAG 4.5:1 directement dans l'architecture.
  • Les tokens sémantiques comme color.text.on-primary ou color.surface.default créent des paires foreground/background dont le contraste est vérifié à la source.
  • Des outils comme style-dictionary, Figma Tokens et des scripts CI permettent de vérifier automatiquement les ratios de contraste à chaque modification de palette.
DesignCSSWCAGAccessibilitéOutils

Un bouton avec du texte blanc sur fond bleu. Le contraste est bon, le designer l'a vérifié. Trois mois plus tard, quelqu'un met à jour la couleur primaire dans Figma. Le bleu devient plus clair. Le bouton passe sous le ratio 4.5:1.

Personne ne s'en aperçoit, parce que la vérification du contraste n'est faite qu'au moment de l'audit.

Ce scénario est courant dans les équipes qui ont un design system. La solution n'est pas de vérifier plus souvent : c'est d'encoder la contrainte dans l'architecture même des tokens.


Comprendre les couches de tokens

La plupart des design systems matures structurent leurs tokens en trois niveaux. Cette architecture n'est pas qu'organisationnelle : elle détermine votre capacité à garantir l'accessibilité à l'échelle.

Tokens primitifs (global)

Les valeurs brutes. La palette complète, toutes les tailles, tous les espacements :

CODE
/* Primitifs — valeurs brutes */
--color-blue-50: #eff6ff;
--color-blue-100: #dbeafe;
--color-blue-500: #3b82f6;
--color-blue-600: #2563eb;
--color-blue-700: #1d4ed8;
--color-blue-900: #1e3a5a;
--color-neutral-0: #ffffff;
--color-neutral-50: #f9fafb;
--color-neutral-900: #111827;

À ce niveau, aucune notion d'usage. color-blue-500 ne dit rien sur l'endroit où cette couleur sera utilisée.

Tokens sémantiques (alias)

C'est ici que l'accessibilité se joue. Les tokens sémantiques expriment une intention et créent des paires de couleurs :

CODE
/* Sémantiques — intention + paires */
--color-text-default: var(--color-neutral-900);
--color-text-muted: var(--color-neutral-600);
--color-text-on-primary: var(--color-neutral-0);

--color-surface-default: var(--color-neutral-0);
--color-surface-subtle: var(--color-neutral-50);
--color-surface-primary: var(--color-blue-600);

--color-border-default: var(--color-neutral-300);
--color-border-focus: var(--color-blue-600);

La convention de nommage text-on-primary est la clé. Elle établit que ce texte sera posé sur la surface primary. Le contraste entre color-text-on-primary et color-surface-primary doit respecter le critère RGAA 3.2 : ratio minimum de 4.5:1 pour le texte normal, 3:1 pour le texte agrandi.

Tokens de composants

Le dernier niveau applique les tokens sémantiques aux composants spécifiques :

CODE
/* Composant : bouton primaire */
--button-primary-bg: var(--color-surface-primary);
--button-primary-text: var(--color-text-on-primary);
--button-primary-border: var(--color-border-default);

À ce stade, le composant hérite de la paire foreground/background validée au niveau sémantique. Si la paire respecte le ratio, le bouton aussi.


Conventions de nommage qui protègent le contraste

Le nommage des tokens n'est pas qu'une question d'élégance. Les bonnes conventions rendent les erreurs de contraste visibles avant même qu'elles ne se produisent.

Le pattern on-*

Quand un token de texte est destiné à être posé sur une surface spécifique, préfixer par on- :

Token texteToken surface associéContraste garanti
color.text.on-primarycolor.surface.primary4.5:1 minimum
color.text.on-successcolor.surface.success4.5:1 minimum
color.text.on-errorcolor.surface.error4.5:1 minimum
color.text.on-surfacecolor.surface.default4.5:1 minimum

Ce pattern rend explicite la dépendance. Quand un designer modifie color.surface.primary, il voit immédiatement que color.text.on-primary existe et doit être vérifié.

Le pattern foreground/background par composant

Pour les composants complexes avec plusieurs zones de texte :

CODE
--card-heading-fg: var(--color-text-default);
--card-heading-bg: var(--color-surface-default);

--card-badge-fg: var(--color-text-on-primary);
--card-badge-bg: var(--color-surface-primary);

Chaque paire fg/bg est vérifiable individuellement.

Ce qu'il faut éviter

Les noms génériques sans indication de contexte :

CODE
/* Mauvais : aucune indication de paire */
--color-1: #2563eb;
--color-2: #ffffff;
--text-color: #333;
--bg-color: #f5f5f5;

Impossible de savoir quel texte va sur quel fond. Les régressions de contraste sont inévitables.


Dark mode : remapper sans casser

Le dark mode est le test ultime de votre architecture de tokens. Si vos tokens sont bien structurés, le passage au thème sombre se fait sans toucher aux composants.

CODE
/* Thème clair (défaut) */
:root {
  --color-text-default: var(--color-neutral-900);
  --color-text-on-primary: var(--color-neutral-0);
  --color-surface-default: var(--color-neutral-0);
  --color-surface-primary: var(--color-blue-600);
}

/* Thème sombre */
[data-theme="dark"] {
  --color-text-default: var(--color-neutral-50);
  --color-text-on-primary: var(--color-neutral-0);
  --color-surface-default: var(--color-neutral-900);
  --color-surface-primary: var(--color-blue-500);
}

Attention : le ratio qui fonctionne en light mode ne fonctionne pas nécessairement en dark mode. color-blue-600 sur blanc donne un contraste de 4.6:1. color-blue-600 sur neutral-900 donne seulement 3.8:1. C'est pour cela que le dark mode remape vers blue-500 (plus lumineux).

Chaque paire sémantique doit être vérifiée dans chaque thème. Notre guide sur le dark mode et l'accessibilité détaille les pièges spécifiques.

[!WARNING] Les ratios de contraste perçus ne sont pas symétriques. Un texte clair sur fond sombre est perçu différemment d'un texte sombre sur fond clair, même à ratio WCAG identique. L'algorithme APCA (futur WCAG 3) corrige ce biais. En attendant, visez un ratio légèrement supérieur au minimum en dark mode.


Automatiser la vérification des contrastes

La vérification manuelle ne passe pas à l'échelle. Trois points d'intégration sont possibles.

Au design (Figma)

Des plugins comme Contrast ou Stark vérifient les contrastes en temps réel dans Figma. Si votre design system utilise Figma Tokens (ou Tokens Studio), les paires foreground/background peuvent être déclarées et vérifiées avant même l'export.

Vérifiez aussi vos maquettes avec notre calculateur de contraste ou le simulateur de daltonisme pour couvrir les déficiences visuelles au-delà du ratio.

Au build (style-dictionary)

Si vous utilisez style-dictionary pour transformer vos tokens en variables CSS, vous pouvez ajouter une action de validation :

CODE
// validate-contrast.js
const { contrast } = require('chroma-js');

function validateContrast(pairs, tokens) {
  const errors = [];
  for (const [fg, bg, minRatio] of pairs) {
    const fgColor = tokens[fg];
    const bgColor = tokens[bg];
    const ratio = contrast(fgColor, bgColor);
    if (ratio < minRatio) {
      errors.push(
        `${fg} sur ${bg} : ratio ${ratio.toFixed(2)} < ${minRatio}`
      );
    }
  }
  return errors;
}

// Paires à vérifier
const pairs = [
  ['color.text.on-primary', 'color.surface.primary', 4.5],
  ['color.text.on-success', 'color.surface.success', 4.5],
  ['color.text.on-error', 'color.surface.error', 4.5],
  ['color.text.default', 'color.surface.default', 4.5],
  ['color.text.muted', 'color.surface.default', 4.5],
];

Le script échoue si un ratio passe sous le seuil. Intégré dans le pipeline CI, il bloque la PR avant le merge.

En production (audit)

Les vérifications au design et au build couvrent les tokens déclarés. Mais rien ne garantit qu'un développeur n'a pas utilisé une couleur en dur, contourné un token, ou composé deux surfaces de manière inattendue.

Un audit automatique sur le site déployé vérifie les contrastes effectifs : les couleurs réellement rendues par le navigateur, avec les héritages CSS, les opacités, et les superpositions.


Intégrer l'accessibilité au-delà des couleurs

Les tokens ne concernent pas que les couleurs. D'autres propriétés de design impactent l'accessibilité :

Tailles de texte. Le critère RGAA 10.4 demande que le texte puisse être agrandi à 200 % sans perte d'information. Les tokens de taille en rem (plutôt qu'en px) respectent ce critère nativement.

CODE
--font-size-sm: 0.875rem;   /* 14px */
--font-size-base: 1rem;      /* 16px */
--font-size-lg: 1.125rem;    /* 18px — texte agrandi : ratio 3:1 suffisant */

Espacements. Le critère WCAG 1.4.12 demande que le contenu reste lisible avec des espacements personnalisés. Des tokens d'espacement cohérents facilitent le respect de cette exigence.

Focus. Les indicateurs de focus doivent être visibles. Un token --focus-ring-color centralisé évite les incohérences :

CODE
--focus-ring-color: var(--color-border-focus);
--focus-ring-width: 3px;
--focus-ring-offset: 2px;

Animations. Les tokens de durée et de mouvement devraient intégrer prefers-reduced-motion. Un token --transition-duration qui passe à 0ms quand l'utilisateur a activé la réduction de mouvement est la meilleure implémentation possible.


Conclusion

Les design tokens sont un levier d'accessibilité sous-estimé. En structurant vos couleurs en paires sémantiques et en automatisant la vérification des contrastes, vous passez d'une conformité vérifiée après coup à une conformité garantie par construction.

Trois choses à faire maintenant : adopter le pattern on-* pour vos tokens de texte, ajouter une vérification de contraste dans votre CI, et lancer un audit sur votre site pour mesurer l'écart entre vos tokens et le rendu réel.

Pour aller plus loin sur les contrastes, consultez notre article Pourquoi le ratio 4.5:1 ne suffit plus qui explore les limites de l'algorithme WCAG actuel et l'arrivée de l'APCA.

Votre site est-il conforme ?

Ne prenez pas de risques avec l'accessibilité. Lancez un audit complet de votre site en quelques minutes et obtenez un rapport détaillé des corrections à apporter.