Étude de cas — Design system
Le design system qui mesure ce qu'il promet
Un design system conçu, développé, mesuré et gouverné seul, où l'accessibilité n'est pas affirmée mais calculée, composant par composant, sur le prototype rendu dans le navigateur.
Contexte
Depuis juin 2025, l'European Accessibility Act est en vigueur. Il rend l'accessibilité numérique obligatoire pour une large part des produits vendus en Europe, et il s'adosse à un référentiel technique précis, EN 301 549, qui reprend WCAG 2.2. Pour un fournisseur qui répond à un marché public, ce n'est plus une qualité souhaitable. C'est une clause.
Un design system est l'endroit où cette exigence se joue. Il fixe les balises, les états, le focus et les contrastes avant qu'aucune page n'existe. Bien fait, il rend la conformité gratuite pour les équipes qui l'utilisent. Mal fait, il leur fait repayer la même dette à chaque écran.
Problème
La plupart des équipes traitent l'accessibilité comme une revue de fin de parcours. On conçoit, on développe, puis on audite, et l'on corrige ce qui peut encore l'être.
Le problème n'est pas le sérieux des équipes, c'est l'ordre des opérations. À l'heure de l'audit, les décisions qui comptent sont déjà prises : la balise retenue, la façon dont un état est annoncé, le contraste du jeu de couleurs, la structure du formulaire. Les reprendre coûte cher, donc on ne les reprend pas toutes.
Un design system peut inverser cet ordre. C'est le seul endroit où la conformité se décide une fois, en amont, pour toutes les équipes qui l'utiliseront ensuite. Encore faut-il qu'il la tienne réellement, et qu'il puisse le montrer plutôt que l'affirmer. C'est le système que j'ai voulu construire : des fondations qui portent le thème, un catalogue bâti sur des éléments natifs, des modèles qui vérifient l'assemblage, et une documentation qui rend le tout utilisable par quelqu'un d'autre que moi.
Challenge 01 — Des tokens qui portent le thème, pas seulement les couleurs
Le défi : construire une base de tokens qui survive à un changement de thème sans que personne ne repasse sur les composants.
Mon approche : deux niveaux stricts. Les primitives
sont des options (une palette, une échelle typographique, des rayons).
Les tokens sémantiques sont des décisions
(surface/background, text/accent,
border/control), et eux seuls sont utilisés dans les
composants. Les modes clair et sombre vivent au niveau sémantique. La
source est Figma, exportée au format DTCG vers un
tokens.css de 3,4 Ko en gzip, deux
thèmes compris.
Résultat : la console de supervision existe en clair et en sombre. La version sombre n'a demandé aucun redessin, aucune couleur ressaisie, aucune variante supplémentaire. Une bascule de mode, et plus de deux cents liaisons se réévaluent. Les contrastes mesurés après bascule tiennent le niveau AAA, entre 9,7:1 et 17,2:1 sur le texte.

Challenge 02 — Un catalogue bâti sur des éléments natifs
Le défi : livrer un catalogue complet sans dépendance, sans framework, et sans que la frugalité se paie en accessibilité.
Mon approche : des règles opposables, écrites avant le premier composant. Zéro dépendance npm. Zéro framework JavaScript. Aucune librairie CSS utilitaire. Pas de JavaScript quand le HTML et le CSS suffisent. Aucune couleur en dur. Focus toujours visible et décalé. Une proposition qui viole une règle doit être justifiée explicitement, ou refusée.
Ces règles ont produit les arbitrages les plus intéressants du projet.
Le <select> natif l'emporte sur une
combobox sur mesure : on hérite gratuitement du
clavier, de la recherche à la frappe, du lecteur d'écran et du sélecteur
mobile, au prix d'une liste ouverte non stylable. Le
<dialog> natif avec showModal() fournit
le piège de focus, le retour de focus, la touche Échap et l'arrière-plan
inerte pour deux lignes de JavaScript. La carte entièrement cliquable
passe par un lien étiré en pseudo-élément, jamais par un
div avec un gestionnaire de clic. Et quand le contraste de
la bordure d'un champ échouait au critère 1.4.11, j'ai créé un
token border/control réutilisable
plutôt que de retoucher une valeur locale : le défaut a été corrigé une
fois, pour tous les composants qui l'ont réutilisé ensuite.
Résultat : neuf des douze composants prototypés fonctionnent sans une ligne de JavaScript. Les trois qui en embarquent le font pour une raison irréductible, et cela se voit dans les chiffres : 606 octets pour ouvrir une modale, 698 pour rendre une infobulle révocable, 1 332 pour alimenter une région live. Chaque composant pèse entre 1,1 et 2,9 Ko en gzip.
Le catalogue compte aujourd'hui dix-sept composants. Les cinq derniers ont été conçus pour un domaine précis, celui des interfaces d'exploitation : tableau de données, badge d'état, bandeau d'alerte persistant, onglets et horodatage. Ils vivent en bibliothèque, avec leurs variantes, leurs états et leurs descriptions d'usage, et ils réemploient les mêmes fondations que les douze premiers.

Challenge 03 — Des critères vérifiés au navigateur, pas déclarés
Le défi : pouvoir dire précisément ce qui est conforme, sur quels critères, avec quel outil, et à quelle date. « Nos composants sont accessibles » n'est pas une réponse recevable dans un dossier de réponse à appel d'offres.
Mon approche : une grille de critères tirés de WCAG 2.2, appliquée composant par composant. Chaque composant en retient huit à dix, et pour chacun il faut statuer : applicable, sans objet, ou écarté parce qu'un autre critère le couvre déjà. Le focus visible, le support clavier complet, la cible tactile, le comportement au zoom 200 %, le nom et le rôle accessibles, le contraste du texte comme des éléments d'interface, l'annonce des changements d'état.
La vérification se fait dans un navigateur, sur le prototype rendu, jamais en analyse statique seule : axe DevTools pour ce qui s'automatise, Lighthouse en corroboration, puis le clavier, le zoom et VoiceOver pour tout le reste. Les analyseurs automatiques ne couvrent qu'une partie des critères ; le reste se teste à la main, ou ne se teste pas.
Résultat : zéro violation axe-core 4.11.4 sur l'ensemble du catalogue prototypé, confirmé au navigateur et daté. La grille produit aussi une note par composant, qui sert à repérer où le système faiblit plutôt qu'à s'auto-décerner un label. Un seul composant n'atteint pas le maximum : le Button, qui sans JavaScript n'annonce pas son changement d'état de chargement. La limite est assumée, mais elle est comptée. Une grille qui n'aurait rien trouvé n'aurait rien prouvé.

Chaque composant porte aussi une seconde note, de poids, construite sur trois variables mesurées sur le prototype rendu : complexité du DOM, requêtes propres, octets transférés. Elle est délibérément séparée de la première. Un indicateur unique aurait dissous les tensions au moment précis où il faut les voir.
Challenge 04 — Se mesurer au mètre-étalon
Le défi : sortir de l'auto-évaluation. Une note qu'on se donne à soi-même ne vaut rien tant qu'elle n'a pas été confrontée.
Mon approche : j'ai reconstruit la même page
d'inscription en état d'erreur deux fois. Une fois avec les composants
DeepFlow, une fois en markup GOV.UK Design System, avec le
bundle réel govuk-frontend 6.2.0. Le
concurrent est choisi contre la facilité :
GOV.UK est la référence mondiale du formulaire
accessible, et il est lui-même frugal. Battre un système
React lourd n'aurait rien démontré.
Résultat : égalité au sommet en accessibilité, zéro violation axe-core des deux côtés. La différence est dans le coût. Pour annoncer les erreurs au chargement, DeepFlow dépense 691 octets de JavaScript, GOV.UK en dépense 11 183. Seize fois moins pour le même résultat. Le DOM est plus économe (52 nœuds contre 57), le CSS livré aussi (11,9 Ko contre 14,8).

Là où je perds, c'est instructif : onze requêtes contre trois, parce que je livre une feuille de style par composant, et un CSS que GOV.UK purge mieux par page. Les deux leviers sont identifiés, chiffrés, et non appliqués. Ils sont dans la feuille de route, pas dans le récit.
Challenge 05 — Passer du catalogue au produit
Le défi : un catalogue conforme ne garantit pas qu'un écran le soit. Il fallait le vérifier plutôt que l'espérer.
Mon approche : deux modèles composés, au niveau produit. Le premier est une page d'inscription en erreur, parce que c'est là que l'accessibilité d'un formulaire se joue vraiment. Le second est une console de supervision, qui met en situation les cinq composants d'exploitation et les fait travailler avec la navigation, les champs et les boutons du catalogue d'origine.
Résultat : les deux modèles ont fait apparaître des exigences qui n'appartenaient à aucun composant.
Sur le formulaire, le résumé d'erreurs n'existait pas au catalogue. Il a
fallu le construire au niveau page. Et l'annoncer s'est révélé plus
subtil que prévu : un role="alert" posé dans le HTML
initial n'est jamais annoncé, puisqu'il ne se déclenche qu'à
l'insertion. La solution robuste consiste à déplacer le focus sur le
résumé au chargement, ce qui demande du JavaScript. Sans lui, la page
reste conforme et utilisable ; le script n'ajoute que l'annonce
proactive.
Les cinq composants de supervision
Planche de bibliothèque montrant les cinq nouveaux composants avec leurs variantes : Status Badge (4 types), Alert (4 types), Tabs (4 états d'onglet actif), Timestamp (3 formats), Table avec ses cellules d'en-tête triables. Mode sombre de préférence, cohérent avec la couverture.
16 : 9
Sur la console, trois décisions se sont imposées de la même façon. La fraîcheur de la donnée est affichée, parce qu'un écran qui se rafraîchit sans dire quand il l'a fait rend toute lecture indatable. L'alerte critique renvoie à la ligne du tableau au lieu de la dupliquer, pour qu'il n'y ait qu'une source de vérité. Et j'ai refusé de poser une région live sur la colonne d'état : elle aurait annoncé un changement toutes les quelques secondes et rendu l'écran inutilisable au lecteur d'écran. Ce refus est une décision de conception, pas un oubli.
Documenter le système avec lui-même
Un design system qu'on ne peut pas consulter n'existe que pour celui qui l'a écrit. La documentation est donc un livrable du système, pas une annexe.
Le site tient en une vingtaine de pages : une prise en main, les fondations, une fiche par composant, les modèles composés, la méthode de vérification, et une recherche interne. Chaque fiche de composant donne à quoi il sert, quand l'employer, ses variantes, sa signature d'accessibilité et le piège à éviter. Il est généré par un build Node d'une centaine de lignes, sans aucune dépendance, et ses sorties sont versionnées : il se déploie en statique, sans étape de construction chez l'hébergeur.
Il est surtout construit avec le système qu'il documente. Ses propres boutons, champs, cartes et barres de navigation sont ceux du catalogue, avec les mêmes tokens et les mêmes modes clair et sombre. C'est le premier produit consommateur du système, et la première occasion de découvrir ce qui manque en s'en servant réellement.

Gouverner un système qu'on est seul à tenir
Un design system sans règles de version est un dossier de fichiers. J'ai écrit les miennes avant d'en avoir besoin : un contrat SemVer qui définit ce qui casse et ce qui n'est qu'un ajout, un journal des modifications, une feuille de route qui sépare ce qui est fait, en cours et prévu, et un gabarit de composant en onze points appliqué aux douze.
S'y ajoute une grille de maturité à trois niveaux, volontairement distincte des deux notes : elle mesure la rigueur d'ingénierie autour du composant, pas sa qualité livrée. L'audit est instructif. Les douze composants prototypés sont au niveau intermédiaire, aucun n'atteint le plus haut, et les trois bloqueurs sont identiques pour tous : internationalisation non vérifiée, tests de régression absents, test utilisateur avec des personnes en situation de handicap non réalisé. Ce ne sont pas des faiblesses de tel ou tel composant, ce sont des manques du système. Lever l'un d'eux ferait progresser tout le catalogue d'un coup.
Résultats
- Des fondations complètes : variables à deux niveaux, modes clair et sombre, tokens exportés au format DTCG vers un
tokens.cssde 3,4 Ko. - 17 composants conçus, dont 12 prototypés en HTML, CSS et JavaScript natifs, documentés et vérifiés au navigateur.
- Zéro violation axe-core sur le catalogue prototypé, confirmée au clavier, au zoom 200 % et au lecteur d'écran.
- Deux modèles composés, dont une comparaison mesurée au GOV.UK Design System : 16× moins de JavaScript à conformité égale.
- Un site de documentation d'une vingtaine de pages, bâti avec le système lui-même et généré sans aucune dépendance.
- Deux thèmes obtenus par bascule de mode, sans duplication de composant.
- Zéro dépendance npm sur l'ensemble du projet, site de documentation compris.
- Une gouvernance écrite : SemVer, journal des modifications, feuille de route, gabarit de processus, grille de maturité.
Ce que le système ne fait pas encore
Trois limites, et je préfère les écrire que les laisser découvrir.
La note de poids est un rang interne, pas un grade absolu. Elle situe un composant par rapport aux onze autres du catalogue. La note d'accessibilité, elle, est absolue. Cette asymétrie est documentée et assumée, mais elle interdit de comparer une lettre de poids à celle d'un autre système.
Les cinq composants de supervision sont conçus, pas encore mesurés. Ils existent en bibliothèque, avec leurs variantes, leurs états et leurs descriptions d'usage. Ils n'ont ni prototype ni fiche de score, donc pas de note. Les leur attribuer par analogie aurait été le contraire de ce que défend ce projet.
Aucun test n'a été mené avec des personnes en situation de handicap. L'audit est technique : axe-core, Lighthouse, clavier, VoiceOver. C'est l'écart connu avec GOV.UK, et il est inscrit en tête de la feuille de route.
Apprentissages
Le choix accessible est presque toujours le plus économe. Je m'attendais à un arbitrage permanent entre conformité et légèreté. Douze fois sur douze, l'élément natif a gagné sur les deux tableaux à la fois. La tension existe, mais elle est ailleurs : dans la police auto-hébergée, qui représente 73 % du poids de la première visite et reste un choix de lisibilité assumé.
Vérifier en amont coûte moins cher que corriger en aval. Le seul écart de conformité trouvé pendant le projet portait sur le contraste d'une bordure de champ, que les analyseurs automatiques ne détectent pas. Il a été corrigé une fois, en créant un token réutilisable, et tous les composants construits ensuite en ont hérité. Découvert trois mois plus tard en revue de fin de parcours, il aurait fallu le corriger partout où il s'était propagé.
L'accessibilité d'un écran ne se délègue pas au catalogue. C'est le résultat qui m'a le plus surpris. Le résumé d'erreurs, la fraîcheur de la donnée, le refus d'une région live trop bavarde : rien de tout cela n'appartient à un composant. Un système peut être irréprochable au détail et laisser passer un écran inutilisable.