É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.

Planche des composants DeepFlow en mode sombre, sur fond presque noir. Neuf surfaces surélevées portent chacune un composant rendu : les quatre variantes de bouton — violet, contour blanc, texte seul, rouge —, deux notifications, l'une verte de confirmation et l'autre rouge d'alerte, un champ e-mail avec son libellé et son aide, une liste déroulante, trois cases à cocher et boutons radio dont deux activés en violet, une carte de contenu, quatre boutons-icônes, et une notification orange d'avertissement.
Rôle
Conception des fondations, design et développement des composants, composition des modèles, vérification d'accessibilité, documentation et gouvernance
Livrables
Bibliothèque Figma · variables à deux niveaux, modes clair et sombre · tokens exportés au format DTCG · catalogue de 17 composants · 2 modèles composés · site de documentation · grille de critères d'accessibilité · gouvernance SemVer

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.

Écran de supervision en thème clair. Une barre de navigation porte les entrées Missions, Flotte, Incidents et Rapports. En dessous, le titre « Mission du 2 août 2026 » et l'heure de dernière mise à jour, puis un bandeau d'alerte signalant la perte de liaison télémétrie du drone 07. Suivent des onglets, deux filtres, et un tableau de cinq appareils donnant pour chacun son état, sa batterie, son altitude et l'heure du dernier contact.Le même écran en thème sombre. Les blocs, la mise en page et les positions sont strictement identiques au panneau précédent : seules les couleurs changent.
Le même écran, deux thèmes. Aucun composant redessiné : les modes vivent dans les tokens sémantiques.
Panneau des variables de la bibliothèque, collection « Semantic — Color ». Chaque ligne est un token de décision, avec une colonne Light et une colonne Dark. Toutes les valeurs des deux colonnes pointent vers une variable primitive : par exemple, le texte par défaut vise neutral 900 en clair et neutral 50 en sombre, la surface de fond vise neutral 0 en clair et neutral 900 en sombre. Aucune couleur n'est saisie directement.
L'architecture à deux niveaux : les primitives sont des options, les tokens sémantiques sont des décisions.

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.

Page d'inspection du composant Button rendue dans un navigateur. Les quatre variantes — primaire, secondaire, tertiaire et danger — sont alignées côte à côte, chacune dans ses deux tailles. Suivent le modificateur pleine largeur, puis les rangées d'états désactivé et chargement, où le libellé cède la place à une icône de progression.
La page d'inspection du Button : quatre variantes, deux tailles, six états, plus le modificateur pleine largeur. Rendue dans le navigateur, pas dessinée.
Page catalogue du site de documentation, thème clair. Les composants sont rangés par usage — Actions, Saisie, Navigation, Conteneurs, Feedback, Contenu — et chacun porte une carte avec son nom, ses deux notes et sa raison d'être en une phrase. Le menu latéral liste aussi les composants à venir, signalés comme tels.La même page en thème sombre. Le classement, les cartes et le menu sont identiques : seules les couleurs changent.
Le catalogue documenté, rangé par usage. Le menu signale aussi ce qui n'existe pas encore : un système honnête dit où il s'arrête.

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é.

Onglet Accessibilité de la fiche Button, sur le site de documentation. La note A 92,6 est rappelée en tête, puis un tableau de neuf critères : chacun porte son intitulé, son impact — moderate, serious ou critical — et son résultat. Huit sont en « pass » vert ; le septième, l'annonce de l'état de chargement, est en « fail » rouge. Un encadré sous le tableau nomme cet échec comme la limite assumée du zéro JavaScript.Le document docs/grille-a11y.md : un tableau de onze critères numérotés, chacun avec sa référence WCAG 2.2, la règle axe-core correspondante quand elle existe — target-size, color-contrast, button-name, image-alt — ou la mention « manuel », et son poids. Deux sections suivent : la convention de pondération, et la discipline de sélection, qui impose de statuer applicable, sans objet, ou écarté pour doublon.
Pour chaque composant : les critères retenus, ceux écartés et pourquoi, la méthode de test et le résultat.

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.

Fenêtre de navigation privée. À gauche, la page d'inspection du Button telle qu'elle est rendue ; à droite, le panneau axe DevTools ouvert sur la même adresse. Le compteur « Total des problèmes » affiche 0, et le détail le confirme ligne par ligne : zéro problème détecté automatiquement, zéro guidé, zéro manuel, zéro en critique, sérieux, modéré et mineur. En pied de panneau, les deux réglages de l'analyse : bonnes pratiques désactivées, norme WCAG 2.2 AA.
La mesure se fait sur le prototype rendu dans un navigateur isolé, jamais dans l'outil de design. Re-vérifié en axe-core 4.12.1 : toujours zéro violation.

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).

Formulaire « Créer un compte » en état d'erreur, version DeepFlow. Un encadré rouge « Il y a un problème » ouvre la page et liste deux erreurs en liens. Suivent les champs : civilité, nom complet, adresse e-mail — encadrée en rouge, avec son message sous le champ —, mot de passe, puis les cases à cocher des conditions et de la lettre d'information, et le bouton violet « Créer mon compte ».Le même formulaire construit avec GOV.UK Design System. Le résumé d'erreurs passe au-dessus du titre, les champs en erreur sont marqués par une barre rouge à gauche plutôt que par un encadré, et le bouton d'envoi est vert et pleine largeur. Les contenus, eux, sont identiques au panneau de gauche.
La même page, en état d'erreur, construite deux fois. À gauche DeepFlow, à droite le mètre-étalon.

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.

Cinq mesures comparées, chacune avec sa propre échelle : les barres violettes sont DeepFlow, les vertes hachurées GOV.UK, et la plus courte est toujours la plus sobre. JavaScript : 691 octets contre 11 183, l'écart le plus spectaculaire de la planche. Nœuds DOM : 52 contre 57. CSS livré : 11 965 octets contre 14 817, avec la mention que GOV.UK, purgé par page, descendrait à environ 4 800. Requêtes HTTP : 11 contre 3, DeepFlow perd. Poids de première visite : 62,8 Ko contre 27, DeepFlow perd aussi — mais hors du domaine gov.uk, où GOV.UK n'a aucune police à charger ; sur son propre domaine il monte à environ 88 Ko. Une note finale rappelle l'égalité en accessibilité : zéro violation axe-core des deux côtés.
Le relevé complet, gains et pertes. Une comparaison qui ne montrerait que les gains ne serait pas une mesure.

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.

Visuel à produire

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

Les cinq composants d'exploitation, conçus pour la console : tableau, badge d'état, bandeau d'alerte, onglets, horodatage.

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.

La console de supervision en mode sombre, portant trois pastilles numérotées, reprises et développées dans une colonne à droite. La première est posée après la ligne « Dernière mise à jour il y a 12 s », sous le titre de la mission : un écran qui se rafraîchit sans dire quand il l'a fait rend toute lecture indatable. La deuxième est dans le bandeau d'alerte rouge, au bout de la phrase « L'appareil est signalé Critique dans le tableau ci-dessous » : l'état vit à un seul endroit, la ligne, et le bandeau y conduit sans la recopier. La troisième est posée sur le badge « Critique » de la ligne Drone 07, dans la colonne d'état : aucune région live n'y a été mise, elle aurait annoncé un changement toutes les quelques secondes et rendu l'écran inutilisable au lecteur d'écran.
Ce que la composition exige, et qu'aucun composant ne portait.

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.

Fiche du composant Button sur le site de documentation : à quoi il sert, quand l'employer, ses variantes rendues en direct, sa signature d'accessibilité et le piège à éviter. Le menu latéral donne l'ensemble des fondations et des composants.Page Couleurs des fondations. Chaque token sémantique est présenté par un aplat de sa couleur réelle, son nom, et ses valeurs en hexadécimal, RVB, TSL et OKLCH. Les couleurs affichées sont lues depuis les tokens du système, elles ne sont pas redessinées pour la documentation.
Le site de documentation est le premier produit consommateur du système : il est bâti avec ses propres composants.

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.

Les douze composants du catalogue en douze lignes, face aux trois niveaux de maturité : Éprouvé, Consolidé, Core. Pour chacun, les deux premiers niveaux sont pleins et le troisième reste vide — douze lignes rigoureusement identiques. Un trait noir continu court sur toute la hauteur, juste avant le niveau Core : le plafond. À sa droite, les trois bloqueurs, nommés une seule fois puisqu'ils valent pour les douze : internationalisation non vérifiée, aucun test de régression visuelle, aucun test avec des personnes en situation de handicap. Lever un seul des trois fait progresser les douze d'un coup.
La grille de maturité mesure la rigueur d'ingénierie autour d'un composant, pas sa qualité livrée. Elle est faite pour déclasser.

Résultats

  • Des fondations complètes : variables à deux niveaux, modes clair et sombre, tokens exportés au format DTCG vers un tokens.css de 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.

Pour échanger sur un projet de ce type