# Frontend — Risques & vigilance : Design Tokens

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

---

<a id="risque-double-systeme-espacement"></a>
## 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

---

<a id="risque-dimensions-image-via-spacing"></a>
## 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

```typescript
// 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

---

<a id="risque-inline-styles-dashboard"></a>
## 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

---

<a id="risque-tailwind-classes-invalides"></a>
## 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

---

<a id="risque-imports-morts-tokens-rn"></a>
## 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

---

<a id="risque-design-tokens-fallback-theme-incoherent"></a>
## 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

---

<a id="risque-design-tokens-variables-non-definies"></a>
## 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-subtle`→`border-soft`), 18-06-2026

---

<a id="risque-theming-light-dark-pieges-caches"></a>
## 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

---

<a id="risque-nouveau-site-colors-pendant-migration-themed"></a>
## 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

---

<a id="risque-hook-themed-sans-consommateur-mort-ne"></a>
## 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

---
