Files
_Assistant_Lead_Tech/knowledge/frontend/risques/design-tokens.md
T
MaksTinyWorkshop 5f5c87296e docs(knowledge): capitalisation frontend — intégration du triage local (mai-juin 2026)
Triage et intégration des propositions frontend du buffer 95_a_capitaliser.md
(lot local RL799_V2/Vue3 + app-alexandrie/RN-Expo, mai-juin 2026).

~73 entrées intégrées sur knowledge/frontend/ + 1 nouveau fichier, dont :
- patterns/state.md : race-token partagé latest-wins (fusion 3 props), capture sync anti-race,
  event bus timestamp, clé cache composite, état dérivé = computed
- risques/state.md : 9 risques Zustand/store (fetchId reset, useRef remount, re-fetch infini
  sur [], flag optimiste écrasé, cache détail/liste stale, latch sans reset, :key index)
- patterns/navigation.md : Expo Router (tab bar, useFocusEffect, Href typé, routing pur fusionné)
- patterns/general.md : helpers temps purs, composants génériques + skeleton, fail-fast, touch target
- risques/general.md : 24 risques (sweep statique, filtre client liste paginée, hooks avant return,
  a11y VoiceOver/disabled, redirection allowlist, RangeError toISOString, section i18n...)
- design-tokens (cluster theming light/dark MD3), tests, performance, react-native, nextjs
- NOUVEAU risques/responsive.md (gating par capacité d'input + checklist régressions mobile)
- READMEs patterns/risques mis à jour

Doublons inter-fichiers évités (vérifié : aucune ancre dupliquée introduite).
Rejets (doublons 91/9/87), reciblages workflow (156/257) et bloc 32 (CLAUDE projet) non intégrés ici.
Source 95_a_capitaliser.md non purgée (purge en fin de capitalisation complète).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 15:31:53 +02:00

11 KiB

Frontend — Risques & vigilance : Design Tokens

Extrait de la base de connaissance Lead_tech. Voir knowledge/frontend/risques/README.md pour l'index complet.


Double système d'espacement dans un monorepo Expo

Risques

  • Deux échelles d'espacement coexistent avec des noms différents pour des valeurs identiques (Spacing.three = 16 vs spacing.base = 16)
  • L'audit "zéro hardcode" ne détecte pas l'inconsistance car les deux sont des constantes nommées
  • Les deux échelles peuvent diverger silencieusement

Symptômes

  • import { Spacing } from '@/constants/theme' coexiste avec import { spacing } from '@/theme'
  • Certains screens refactorisés utilisent l'ancien système sans que personne ne le détecte

Bonnes pratiques / mitigations

  • Dès la création de src/theme/spacing.ts, supprimer ou vider constants/theme.ts (sauf constantes vraiment spécifiques : MaxContentWidth, BottomTabInset)
  • Faire un grep from '@/constants/theme' à chaque story pour détecter les usages résiduels
  • Cause racine : le template Expo génère constants/theme.ts avec Spacing = { one, two, three... } — à purger explicitement lors de la story design tokens
  • Contexte technique : Expo / React Native — app-alexandrie story 0.5, 19-03-2026

Dimensions d'image via tokens spacing (React Native)

Risques

  • Si spacing.huge change pour une raison d'espacement, la taille de l'image change silencieusement
  • Régression visuelle sans que personne ne réalise l'impact — les deux changements semblent indépendants

Symptômes

  • width: spacing.huge, height: spacing.huge pour une image dont la taille est fixée par la spec Figma

Bonnes pratiques / mitigations

// Correct : constante locale ou token dédié
const THUMBNAIL_SIZE = 48; // Figma spec node 1-16147

// OU token dans un fichier sizes.ts dédié si la valeur est partagée
export const sizes = { thumbnail: 48, avatar: 40 } as const;

Règle : spacing = espacement entre éléments. sizes ou constantes locales = dimensions de composants.

  • Contexte technique : React Native / design tokens — app-alexandrie story 0.4, 19-03-2026

Inline styles dans les composants dashboard

Risques

  • Contourne le système Tailwind + tokens CSS
  • Crée des incohérences visuelles non détectées par le linter

Symptômes

  • style={{ color: '#123456', marginTop: 8 }} dans un composant dashboard

Bonnes pratiques / mitigations

  • Bloquer en code review systématiquement tout style={{...}} dans les composants dashboard

  • Exception acceptable uniquement : animations CSS dynamiques (valeurs calculées au runtime)

  • Contexte technique : React / Tailwind — app-template-resto 22-03-2026


Classes Tailwind invalides courantes (bugs silencieux)

Risques

  • Classes Tailwind invalides sont silencieusement ignorées — aucun warning, comportement visuellement cassé

Symptômes

  • Item masqué affiché à pleine opacité (opacity-55 → invalide)
  • Largeur incorrecte (w-35 → invalide)

Bonnes pratiques / mitigations

Erreurs courantes :

  • opacity-55 → invalide. Scale : 0/5/10/20/25/30/40/50/60/70/75/80/90/95/100 → utiliser opacity-50 ou opacity-60

  • w-35 → invalide. Scale saute de w-32 à w-36 → utiliser w-36

  • box-border → redondant. Tailwind Preflight applique déjà box-sizing: border-box globalement

  • Toujours vérifier les classes custom/non-standard avec l'extension Tailwind IntelliSense

  • Contexte technique : Tailwind CSS — app-template-resto 22-03-2026


Imports morts de tokens dans les composants React Native

Risques

  • Les imports de tokens abandonnés (ex : fontWeight après passage aux fontes nommées par variante) ne génèrent pas d'erreur TypeScript car le type est compatible avec les usages implicites
  • La migration de design system peut laisser des dépendances obsolètes indétectables au build

Symptômes

  • import { fontWeight } from '@/tokens' présent mais inutilisé — aucun lint warning sans règle dédiée
  • Tokens refactorisés encore référencés dans des composants après migration

Bonnes pratiques / mitigations

  • Activer @typescript-eslint/no-unused-vars et no-unused-imports dans la config ESLint mobile
  • Lors de toute migration de tokens, auditer les imports de chaque composant UI concerné
  • Contexte technique : React Native / ESLint — app-alexandrie, 25-03-2026

Fallbacks de tokens incohérents avec le thème actif

Risques

  • Composants lisibles en dev mais illisibles quand une variable manque en production.

Symptômes

  • Fallback hardcodé light-theme dans un contexte dark-theme.

Bonnes pratiques / mitigations

  • Utiliser des fallbacks cohérents avec le thème de référence.

  • Préférer un fallback vers alias sémantique existant plutôt qu'une couleur brute.

  • Contexte technique : design tokens / thèmes CSS — RL799_V2 10-04-2026


Variables CSS utilisées mais non définies (fallback silencieux)

Risques

  • Dérive invisible du design system (les fallbacks masquent les oublis de token).

Symptômes

  • L'UI semble correcte mais contourne les tokens sur une part significative des règles.

Bonnes pratiques / mitigations

  • Auditer régulièrement : variables utilisées moins variables définies.

  • Traiter chaque variable non définie comme dette explicite.

  • Contexte technique : design tokens / audit CSS vars — RL799_V2 17-04-2026

Généralisation : token inexistant sans fallback = propriété silencieusement non appliquée

  • Le piège vaut pour toutes les familles de tokens, pas seulement la scale d'espacement (--rm-space-0-5). Cas traître : border: 1px solid var(--rm-color-border-subtle) quand le token n'existe pas et n'a pas de 2ᵉ argument de fallback → border-color retombe sur sa valeur initiale currentColor (couleur du texte) : la bordure s'affiche mais avec la mauvaise couleur, encore plus discret qu'une bordure absente.
  • Règle : avant d'utiliser un nom de token "par analogie" (-subtle, -muted, -faint), grep le nom EXACT dans le fichier de thème (grep -- '--rm-color-border-subtle:' theme.css). La nomenclature n'est pas devinable (RL799 a border-base/strong/soft/panel/accent, PAS subtle).
  • Un token fantôme déjà présent dans N autres fichiers n'est PAS une excuse : il propage le bug. Le corriger ponctuellement + noter la dette pour un nettoyage transverse.
  • Détection review : pour chaque var(--rm-…) SANS 2ᵉ argument dans un fichier touché, vérifier l'existence de la définition.
  • Contexte technique : Vue 3 / CSS — RL799 (code review v2-2-1, border-subtleborder-soft), 18-06-2026

Theming light/dark : pièges cachés invisibles aux tests unitaires

Risques

  • La majorité des bugs de migration light/dark ne sont visibles qu'au smoke device : typecheck, lint et tests unitaires passent sans warning
  • Trois familles de causes : composants tiers non thématisés, <Text>/<TextInput> sans couleur explicite, et mauvaise résolution du scheme effectif

Symptômes

  • Écran 80 % migré mais un bloc reste noir/illisible en dark
  • Texte ou placeholder invisible sur fond sombre malgré le remplacement de tous les hex

Bonnes pratiques / mitigations

  • Composants tiers (Markdown, charts, WebView) : ils appliquent leurs styles par défaut (color: '#000') qui n'héritent ni de React Navigation ni du hook. Passer un objet style mappant explicitement aux tokens (body, paragraph, link, blockquote, code_inline). Détection : grep -rln "Markdown\|svg-charts\|WebView".
  • <Text> sans color: : en RN Web, hérite du défaut OS → invisible sur fond sombre. Détection : grep -nB1 "<Text" file.tsx | grep -v "color:". Ajouter color: themed.onSurface (ou onSurfaceVariant pour metadata).
  • <TextInput> sur RN Web : color ET placeholderTextColor sont obligatoires ensemble, sinon input/placeholder illisibles en dark.
  • Préférence 'system' + toggle binaire : isDark = preference === 'dark' est faux quand preference === 'system' + OS dark (rendu dark mais isDark === false). Utiliser isDark = useEffectiveColorScheme() === 'dark' (refléter le rendu effectif, pas la préférence brute).
  • ThemeProvider React Navigation : DefaultTheme/DarkTheme posent leurs colors.card/text au-dessus des composants. Construire un Theme custom à partir des tokens (background, card, text, border, notification).
  • Anti-pattern global : laisser le smoke device comme seul filet. Le filet préventif est un grep méthodique post-migration.
  • Contexte technique : React Native / Expo — app-alexandrie (ux-cleanup-8), 29-05-2026

Nouveau site colors.X introduit pendant une migration useThemedColors

Risques

  • Pendant qu'une story migre vers useThemedColors (light/dark), une autre story crée un nouveau fichier qui utilise import { colors } from '@/theme' (palette dark figée) + backgroundColor: colors.background
  • Typecheck et tests passent ; le mode light cassé n'est visible qu'à l'œil. Dette héritée par la story de migration

Symptômes

  • +not-found.tsx créé pendant une migration utilise colors.X au lieu de c.X (résultat de useThemedColors)

Bonnes pratiques / mitigations

  • Toute story qui crée un nouveau fichier de rendu pendant une migration thème active utilise useThemedColors d'office, même hors scope de la migration
  • Review : grep from '@/theme' sur les fichiers ajoutés du diff ; colors.X au lieu de c.X = finding HIGH
  • Idéalement : ESLint no-restricted-imports interdisant l'import nommé colors depuis @/theme dans src/app/ et src/components/ (sauf opt-out commenté)
  • Contexte technique : React Native — app-alexandrie (ux-cleanup-4, +not-found.tsx), 29-05-2026

Hook themed sans consommateur = mort-né

Risques

  • Introduire useThemedXxx() (couleurs, shadows, typo) sans migrer aucun caller dans le même commit : le hook est invisible à la review (aucun diff ne montre son usage)
  • Les composants import { xxx } from '@/theme' restent en static (= dark en light) ; la prochaine PR ré-écrit en static croyant que c'est la convention

Symptômes

  • use-themed-shadows.ts livré avec 0 caller pendant 4 commits ; 3 callers importaient encore shadows.button static

Bonnes pratiques / mitigations

  • Un PR qui ajoute un hook themed DOIT migrer ≥ 1 consommateur réel + ajouter un test prouvant la différence light/dark (shadowsLight.card.shadowColor !== shadowsDark.card.shadowColor)
  • Contexte technique : React Native — app-alexandrie (ux-cleanup-8), 29-05-2026