Forme DS

Adoption

Conformité

Ces règles sont vérifiables : un relecteur (ou un agent) peut les cocher une par une sur une diff. Elles ne sont pas des préférences de style — ce sont les conditions mécaniques de la parité. Une PR qui les respecte reste dans la ligne ; une PR qui les enfreint la casse.

RègleCe qu'on vérifie
API label là où un nom accessible est requis Aucun IconButton, aucun contrôle icône sans label. Le glyphe texte ×/✕ est banni : icône Lucide + label « Fermer ».
Pas de #hex en dur — tokens uniquement Aucun #hex ni rgb() en dur dans le TSX/CSS d'app. Exceptions légitimes : email/PDF, logos tiers, data-viz où la couleur porte une donnée.
15px interdit — échelle typo respectée Aucun 15px (→ var(--fs-body)). Aucune valeur hors échelle (17/19/22 en dur → token le plus proche).
Pas de border-left décorative bricolée Aucune border-left 3px décorative sur bloc coloré. La blockquote typographique reste légitime.
Un composant = @forme-ch/ui, jamais dupliqué Aucun composant d'UI réécrit dans l'app, aucun style inline qui contourne le socle (padding pour bricoler un carré, popover fait main).
Radius via tokens Aucun rayon en px en dur. Seule exception : 50% / 9999px pour cercles purs (avatars, points de statut, switch).

La checklist

☐ API label là où un nom accessible est requis
Conforme : Tout contrôle sans texte visible (icône seule, bouton de fermeture) porte un label.
On vérifie : Aucun IconButton, aucun contrôle icône sans label. Le glyphe texte ×/✕ est banni : icône Lucide + label « Fermer ».
☐ Pas de #hex en dur — tokens uniquement
Conforme : Couleurs référencées via var(--color-*). Le thème sombre suit automatiquement.
On vérifie : Aucun #hex ni rgb() en dur dans le TSX/CSS d'app. Exceptions légitimes : email/PDF, logos tiers, data-viz où la couleur porte une donnée.
☐ 15px interdit — échelle typo respectée
Conforme : Tailles prises dans l'échelle : 10/11/12/13/14/16/18/20/28/48, via les tokens --fs-*.
On vérifie : Aucun 15px (→ var(--fs-body)). Aucune valeur hors échelle (17/19/22 en dur → token le plus proche).
☐ Pas de border-left décorative bricolée
Conforme : Un bloc teinté = fond color-mix + filet 1px complet (langage Badge). Le statut est porté par l'icône colorée.
On vérifie : Aucune border-left 3px décorative sur bloc coloré. La blockquote typographique reste légitime.
☐ Un composant = @forme-ch/ui, jamais dupliqué
Conforme : Le composant vient du paquet. Une lacune se corrige dans le paquet, pas dans l'app.
On vérifie : Aucun composant d'UI réécrit dans l'app, aucun style inline qui contourne le socle (padding pour bricoler un carré, popover fait main).
☐ Radius via tokens
Conforme : Rayons pris dans --radius-inner / --radius-element / --radius-container.
On vérifie : Aucun rayon en px en dur. Seule exception : 50% / 9999px pour cercles purs (avatars, points de statut, switch).

Exemples avant / après

// ✗ Non conforme — icône sans nom accessible, glyphe texte.
<button onClick={close}>✕</button>

// ✓ Conforme — icône Lucide + label imposé.
<IconButton label="Fermer" icon={<XIcon />} variant="ghost" size="sm" />
/* ✗ Non conforme — hex en dur, 15px hors échelle, border-left décorative. */
.alerte { color: var(--color-text-red); font-size: var(--font-size-base); border-left: 3px solid var(--color-error); }

/* ✓ Conforme — tokens, échelle typo, filet complet. */
.alerte {
  color: var(--color-error);
  font-size: var(--fs-body);
  border: 1px solid var(--color-error-muted);
  border-radius: var(--radius-element);
}
Réflexe avant de styler. Grep un sélecteur avant de le créer : il peut déjà exister, divergent (piège classique des refontes successives). Et rappelle-toi : la bonne réponse à une lacune du socle n'est jamais du CSS d'app — c'est un enrichissement du paquet.