Aller au contenu

BRI-1406 — Conventions de nommage requises pour le matching Figma ↔ code

Livrable design du projet Design system — notif publish Figma & conventions de nommage (design). But : poser les règles qui permettent au moteur de comparaison (côté dev, BRI-1404) d'apparier un token/composant Figma avec son équivalent code sans faux positifs. Sources : extraction Uimmo du 2026-06-11 (table tokens BRI-304) + artefacts réels du V0 Design QA sur le composant Helper (projects/design-qa-figma-vs-app/).


0. Principe directeur (à lire avant tout le reste)

On matche sur la valeur résolue, jamais sur le nom seul. Le nom sert à apparier deux entités ; la valeur résolue sert à décider s'il y a drift. Trois raisons, toutes vérifiées sur le DS :

  1. Collisions de noms entre collections. lg, sm, xl… existent simultanément dans Spaces (gap/padding), Radius (coins) et Sizes (icônes/largeurs), avec des valeurs différentes : lg = 12 en Spaces, 16 en Radius, 24 en Sizes. Un match par nom seul apparie des choses sans rapport. → toujours qualifier par collection + comparer la valeur.
  2. Le nom est instable, la key ne l'est pas. Chaque variable Figma a une key immuable ; le nom peut être renommé — c'est justement un type de drift qu'on veut détecter. Le matching interne à Figma (versions, dédup) se fait par key. Mais le code n'a pas ces clés → le pont Figma↔code repose forcément sur une convention de nommage partagée, d'où ce document.
  3. Le nom court ment. get_variable_defs affiche le nom court (defaultHeading) alors que la variable réelle est groupée (Typography/defaultHeading). Ne jamais matcher sur le nom court seul.

1. Le « pont » réel : la CSS custom property

Quand Figma génère du code (get_design_context), il émet la valeur sous forme de variable CSS : var(--lg, 16px), background: var(--info-state/infobackground50). Côté code, le thème Tailwind/Uniwind résout ses classes vers ces mêmes customs properties. Le dénominateur commun exploitable est donc le nom de la CSS var (secondairement la valeur résolue).

Transformation observée : nom de variable Figma → nom CSS

Variable Figma (nom complet) CSS var émise par Figma Règle
Bricks specific/brandText --bricks-specific/brandtext groupe kebab-case + / + token tout en minuscules
Bricks specific/brandBackground50 --bricks-specific/brandbackground50 idem (le B de Background disparaît)
Info state/infoBackground50 --info-state/infobackground50 espace → -, minusculisé
Success state/successText --success-state/successtext idem
Backgrounds/neutralBackground50 --backgrounds/neutralbackground50 idem
Typography/defaultHeading --defaultHeading ⚠️ groupe Typography/ DROPPÉ + casse conservée
Typography/defaultText --defaultText ⚠️ idem

⚠️ Incohérence majeure à gérer : la plupart des groupes sont conservés en préfixe kebab minuscule, sauf Typography/ qui est supprimé et garde le camelCase. Le moteur ne peut donc pas appliquer une transformation uniforme — il lui faut une fonction de normalisation (cf. §6) avec ces cas particuliers codés en dur, OU matcher sur la valeur résolue en repli quand la normalisation du nom échoue.

✅ Côté code (confirmé lead dev front, 2026-06-30)

Les noms côté code sont les MÊMES qu'en Figma, mais en vrai kebab-case : infoBackground50info-background-50, Info stateinfo-state. Là où Figma émet une bouillie minuscule sans séparateurs (infobackground50), le code sépare proprement les mots et les chiffres.

La clé de matching canonique est donc le kebab-case. Le moteur normalise les deux côtés en kebab-case (couper le camelCase + insérer un - avant les groupes de chiffres) puis compare. Le code étant déjà en kebab-case, c'est surtout le nom Figma qu'il faut transformer. La seule exception reste Typography/ (groupe droppé) → canonique default-heading, default-text.

Fichiers à parser côté code : common/front/theme/* (couleurs, spacing, radius, typo).


2. Règles par catégorie

2.1 Couleurs

  • Côté Figma : collection Colors (~85 variables), noms groupés Groupe/tokenCamelCase. Groupes : Bricks specific, Typography, Core, Borders, Backgrounds, Success state, Alert state, Info state, Pending state, Disabled state, Shiny, Transparency.
  • Rampes d'état : chaque état sémantique (success/alert/info/pending) suit le schéma xxxText · xxxBorder · xxxBackgroundNNN avec NNN ∈ {50,100,200,…,950}.
  • Règle de matching : normaliser le nom Figma → CSS var (cf. §1), apparier, puis comparer la valeur hex/rgb résolue avec tolérance (égalité hex stricte, ou ΔE faible). Les valeurs code sont souvent en rgb() → convertir en hex avant comparaison.
  • Pièges confirmés :
  • Info state a deux bordures numérotées (infoBorder600, infoBorder950) alors que les autres états ont un seul xxxBorder non numéroté (choix design assumé, pas un drift). Le moteur doit tolérer des schémas par état différents.
  • Core/white existe dans Colors et dans Primitives : c'est un alias légitime (la version Colors pointe vers la primitive), pas un drift.

2.2 Typographie

  • Côté Figma : styles de texte (pas des variables) — familles Heading/* (Inter Semi Bold) et Text/* (Inter Medium). Paliers : Heading/{3xs,2xs,xs,sm,md,lg,xl,2xl,3xl}, Text/{xs,sm,md,lg}.
  • Le style porte police / taille / interligne / letter-spacing ; la couleur est séparée (token Colors). Le matching typo et le matching couleur sont donc deux comparaisons distinctes sur le même nœud.
  • Piège résolution : letter-spacing est stocké en % dans Figma (-2%) et doit être résolu en px selon la font-size avant comparaison : 16px→-0.32px, 14px→-0.28px, 12px→-0.24px. Comparer des % à des px = faux positif garanti.
  • font-weight : Figma attend 600 (Semi Bold) sur titres ET messages du Helper. ⚠️ (le code rendait 400 — drift réel détecté en V0.)
  • ✅ Côté code (confirmé) : chaque style de texte = une className dédiée qui applique tout d'un coup (police + taille + poids + interligne + letter-spacing). 1 style Figma (Heading/sm) ↔ 1 className. Le matching typo se fait donc style ↔ className (1:1), puis vérification des valeurs résolues que la classe applique. (Lister les classNames exactes en parsant common/front/theme/* côté extracteur code.)

2.3 Espacements, rayons, tailles

  • Trois collections distinctes, échelles partiellement homonymes :
  • Spaces (gap/padding) : 3xs=2 · 2xs=4 · xs=6 · sm=8 · md=10 · lg=12 · xl=16 · 2xl=20 · 3xl=24 · 4xl=32 · 5xl=40 · 6xl=48 · 7xl=56 · 8xl=64
  • Radius (coins) : 3xs=2 · 2xs=4 · xs=6 · sm=8 · md=12 · lg=16 · xl=24 · 2xl=32 · full=9999
  • Sizes (icônes, largeurs) : 2xs=8 · xs=12 · sm=16 · md=20 · lg=24 … container-{sm..2xl}=400..1200
  • Règle de matching impérative : ne jamais apparier lglg sans connaître la collection (= le rôle CSS : gap/padding → Spaces, border-radius → Radius, width/height/icône → Sizes). search_design_system renvoie variableCollectionName → filtrer dessus. Côté code, déduire la collection de la propriété CSS ciblée.
  • Pièges confirmés (Helper) : padding lg rendu asymétrique côté code (16px haut/bas, 12px gauche/droite) alors que Figma attend 12px partout → comparer les 4 côtés séparément, pas une valeur unique.

2.4 Composants & variantes

  • Côté Figma : nom de composant en PascalCase, variantes via props régulières State=… , Style=… , Size=…. Les valeurs de variantes peuvent être en français + PascalCase (Alerte, Pending, Neutral).
  • Côté code (Storybook) : args en anglais minuscule (alert, pending, neutral). Les valeurs de taille sont identiques des deux côtés (md, lg).
  • Règle : apparier (composant, variante) via une table de normalisation props-à-props, casse insensible, FR→EN. Table de référence (Helper, étendue au DS) :
Valeur Figma Valeur code (args)
Alerte alert
Pending pending
Info info
Success success
Neutral neutral

→ toute nouvelle valeur de variante doit être ajoutée ici. C'est le cœur du matching composant : si la normalisation est fausse, on compare des variantes qui ne se correspondent pas.

2.5 Icônes

  • Côté Figma : set type Iconsax, noms en kebab-case par famille (wallet-*, empty-wallet-*, card-*, money-*, arrow-*, chart-*…).
  • ✅ Côté code (confirmé) : la lib code EST Iconsax (iconsax.io) — même source que Figma. → mapping 1:1 par nom Iconsax (kebab-case), pas de table de traduction nécessaire. Vérifier surtout la présence/absence d'une icône d'un côté et la variante (linear/bold/etc.).

3. Cas de renommage & alias

Cas Traitement
Renommage d'un token (nom change, valeur identique) C'est un drift de nom légitime à signaler. Détecté en comparant la key (stable) à l'historique côté Figma ; côté code, repérable par « même valeur, nom normalisé différent ».
Alias (Colors/Core/white → primitive white) Non-drift. Maintenir une liste d'alias connus pour ne pas remonter de faux écart.
Casse incohérente (brandborder500 vs brandBorder300/400) Normaliser en comparaison insensible à la casse. (Ce cas précis a été corrigé dans Figma → brandBorder500.)
Tokens multi-rôles (disabledBackground = border ET background) Un token → plusieurs propriétés CSS. Matching 1:1 impossible ; documenter le multi-usage et ne pas trancher automatiquement.

4. Hors scope du matching (à exclure explicitement)

  1. Lib 🖥️ WebApp (legacy) : plus utilisée, pur legacy. Expose des homonymes (colors/white, Text/md/md - Medium, gray-secondary…). L'extracteur Figma DOIT filtrer sur le libraryKey Uimmo sinon faux drifts massifs. Candidate à l'archivage/dépublication.
  2. Kits publics (Material 3, iOS…) : jamais comparés.
  3. Doublons inter-bibliothèques Uimmo ↔ WebApp : ignorer la version WebApp.
  4. Collection Primitives : couche brute sous les tokens sémantiques Colors. La source de vérité pour la comparaison au code est la couche sémantique (Colors), pas les primitives.
  5. Transparency/ghost* et Shiny/* : rampes spéciales/incomplètes — à comparer seulement si un équivalent code existe, sinon hors scope (ne pas générer de drift « absent en code » par défaut).

5. Pièges de résolution avant comparaison (récap)

À normaliser des deux côtés avant tout diff, sinon faux positifs :

  • Couleur : tout convertir en hex minuscule (rgb(236,82,35)#ec5223).
  • letter-spacing : résoudre le % Figma en px selon la font-size.
  • padding : comparer les 4 côtés séparément (asymétries réelles).
  • Nom de token : passer par la fonction de normalisation (§6), pas une égalité brute.
  • font-weight : entier (600) des deux côtés.

6. Spéc de la fonction de normalisation (pour le moteur, BRI-1404)

Contrat attendu pour apparier un token Figma à un token code :

Clé canonique = kebab-case (le code est déjà en kebab-case ; on y ramène le nom Figma).

toKebab(name) -> clé canonique
  // "Info state/infoBackground50" -> "info-state/info-background-50"
  // 1) split sur "/" -> [groupe, token]
  // 2) groupe: espaces -> "-", minuscules                 ("Info state" -> "info-state")
  // 3) token: couper le camelCase + insérer "-" avant les groupes de chiffres, minuscules
  //          ("infoBackground50" -> "info-background-50")
  // 4) CAS PARTICULIER: groupe == "Typography" -> drop groupe
  //          ("Typography/defaultHeading" -> "default-heading")
  // 5) recoller "groupe/token" (ou "token" pour Typography)

matchToken(figmaVar, codeVar):
  // a) apparier par clé canonique kebab-case (les deux côtés normalisés)
  // b) TOUJOURS qualifier par collection/rôle CSS (Spaces|Radius|Sizes|Colors)
  // c) décider du drift sur la VALEUR RÉSOLUE, pas sur le nom
  // d) si nom non résolu mais valeur unique identique -> apparier + signaler "nom divergent"

Règle d'or : nom pour apparier, valeur pour juger. En cas d'ambiguïté de nom, ne jamais conclure au drift sur le seul nom.


7. Réponses lead dev front (2026-06-30) — questions clôturées

  1. Noms côté code ✅ : mêmes noms qu'en Figma, en kebab-case (pas de mapping type bg-info-50). → clé de matching = kebab-case (cf. §1 et §6).
  2. Typographie ✅ : variants dédiés en className — une className applique tout le style. Matching style Figma ↔ className (1:1).
  3. Icônes ✅ : lib code = Iconsax (iconsax.io), même source que Figma → mapping 1:1 par nom.
  4. md dans Radius ✅ : 12.
  5. Fichiers tokens code ✅ : common/front/theme/* (à parser par l'extracteur code, BRI-1403).

Reste à produire côté dev (pas un bloquant de cette issue) : remplir la colonne « code » de la table BRI-304 en parsant common/front/theme/*, et en déduire le score de sync V0. La convention de nommage, elle, est figée.