Design system : jetons, thème, catalogue
Trois couches, et une règle : aucune couleur en dur dans un composant.
| Couche | Fichier | Ce qu'elle porte |
|---|---|---|
| Charte de l'instance | instances/<id>/research.config.ts | 4 couleurs (primary, accent, ink, surface), 2 polices, logo |
| Thème Mantine | src/theme/index.ts, colors.ts | Échelles de 10 nuances, typographie, rayons, variantes accessibles |
| Jetons CSS | src/theme/tokens.css | Fonds, texte, traits, anneau de mise au point, mesure de lecture |
La charte pilote tout le reste
brand.colors ne contient que quatre valeurs. Les échelles Mantine en sont calculées
(colorScale) : la nuance 6 est la couleur exacte, 0 à 5 sont éclaircies vers le blanc, 7 à 9
assombries. Les variantes light et outline prennent la nuance 9 pour tenir 4,5:1, quelle que
soit la charte fournie (variantColorResolver).
Changer l'apparence d'une instance, c'est donc changer quatre hexadécimaux — pas retoucher des composants.
Les jetons CSS
src/theme/tokens.css est la source unique des valeurs qui ne passent pas par Mantine. Le
catalogue les liste et les montre : story Fondations / Charte → Jetons.
--surface fait exception : la mise en page racine l'écrase sur <body> avec la couleur de
l'instance. La valeur du fichier sert de repli, notamment dans Storybook.
Avant cette mise au propre, trois fonds blancs étaient écrits en dur (bg="white",
rgba(255,255,255,0.96) pour l'en-tête) : une instance à la charte colorée obtenait un site
partiellement blanc. C'est corrigé ; le linter ne l'empêche pas encore, c'est une discipline de
relecture.
Le catalogue de composants
Code
Storybook monte exactement l'enveloppe de l'application (AppProviders : thème Mantine +
dictionnaire), si bien qu'une story rend ce que rend une page.
- Fondations / Charte : échelles de couleur calculées, jetons, typographie, grilles.
- Composants : cartes, en-tête de page, fil d'Ariane, état vide, liens et boutons.
Deux réglages utiles :
- la bascule de langue dans la barre d'outils rend la story en français ou en anglais ;
NEXT_PUBLIC_RESEARCH_INSTANCE=paris1 pnpm storybookaffiche l'autre charte — c'est le moyen le plus rapide de vérifier qu'un composant ne dépend pas des couleurs de Saclay.
L'extension d'accessibilité (@storybook/addon-a11y) est active : chaque story affiche le rapport
axe de son propre rendu.
Visualisation et tableau de bord interne
packages/analytics-ui porte les composants de graphique (Mantine + Recharts). Il ne collecte
rien — c'est packages/analytics qui collecte — et il ne connaît pas les données du projet : on
lui passe des tableaux de valeurs.
Le tableau de bord qui s'en sert est /admin : couverture des fiches publiées champ par champ,
état des sources, lacunes à arbitrer, couverture à la moisson par source, et conformité à la
référence figée.
Code
Sans cette variable, la route répond 404 et ne contient aucune donnée : le build produit bien
un fichier à cette adresse, mais c'est la page « introuvable », pas le tableau de bord — vérifié
sur la sortie de build (7 Ko, rien du contenu). La page montre les écarts avec le RNSR et les liens morts — rien de personnel,
mais rien qui doive se trouver sur le site public avant arbitrage. robots.txt l'exclut aussi,
par ceinture et bretelles.
Trois règles tenues dans ces composants, et leur raison :
- une seule teinte par graphique. La longueur encode déjà la grandeur ;
- un statut n'est jamais une couleur seule. Le vert et le rouge employés ici sont à ΔE 4,1 en deutéranopie : chaque statut porte une icône et un mot ;
- deux jeux de couleurs de statut : l'un pour les aplats (3:1 contre la surface), l'autre, plus sombre, pour le texte (4,5:1). Mesuré, pas estimé — la première version échouait à l'audit axe sur huit badges.
Chaque graphique a une bascule « Tableau » et un role="img" résumant ses valeurs : la page
reste lisible sans voir les couleurs.
Un piège de composant serveur
Table.Thead, Table.Td et les autres sous-composants Mantine ne traversent pas la frontière
serveur : depuis un composant serveur, ce sont des propriétés statiques d'une référence de
module client, donc undefined, et le rendu échoue sur « Element type is invalid ». Tout usage de
Table.* vit dans un fichier marqué 'use client' — c'est la raison d'être de
src/app/admin/GapsTable.tsx.
Écrire une story
Les données d'exemple vivent dans src/components/data-display/fixtures.ts et imitent les fiches
réelles — textes longs, acronymes, ordres de grandeur. Une story qui ne montre que le cas idéal ne
sert à rien : cards.stories.tsx rend aussi une fiche sans acronyme, sans présentation et à
compteurs nuls, et une grille où les écarts de hauteur se voient.