Files
_Assistant_Lead_Tech/knowledge/frontend/patterns/design-tokens.md
T
MaksTinyWorkshop 16707b6aaf chore(sanitization): nettoyage repo post-capitalisation
- Suppression de 3 fichiers *.tmp versionnés (résidus install BMAD 6.0.4, vides)
- Ajout de *.tmp au .gitignore
- Déduplication: entrée "Prisma init au chargement = casse build Next.js"
  était dupliquée à l'identique dans risques/prisma.md ET risques/nextjs.md
  → conservée dans nextjs.md (risque Next.js), retirée de prisma.md
- Déduplication: pattern "Tests de styles RN sans renderer JSX" dupliqué dans
  patterns/tests.md ET patterns/design-tokens.md → conservé dans tests.md
- Correction README backend/risques : retrait des entrées fantômes de la ligne
  prisma.md ("init module build" déplacé vers nextjs ; "suppression champ .map()"
  qui vit en réalité dans general.md)

Audit complet de la base : aucun doublon d'ancre restant, liens non cassés,
README synchronisés, format risques/patterns constant. Branches obsolètes
mcp_test et mcp_v1 supprimées (mergées dans main).
2026-06-26 10:49:47 +02:00

10 KiB

Frontend — Patterns : Design Tokens

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


Pattern : Design Tokens natifs TypeScript (Expo / React Native)

Synthèse

  • Objectif : centraliser les tokens de design sans librairie externe (NativeBase, Tamagui), typés et barrel-exportés.
  • Contexte : app Expo / React Native avec un système de design à maintenir.
  • Quand l'utiliser : dès le début d'un projet mobile, avant les premiers composants.
  • Quand l'éviter : si une librairie UI opinionée est déjà choisie et gère ses propres tokens.

Analyse

  • Avantages :
    • aucune dépendance externe, zéro configuration magique
    • autocomplétion TypeScript exacte via as const + types dérivés
    • facile à migrer vers un design system plus élaboré ultérieurement
  • Limites / vigilance :
    • les fichiers TTF doivent être présents dans assets/fonts/ — Google Fonts ne peut pas être téléchargé automatiquement, documenter comme pré-requis dans la story
    • ne pas réutiliser les tokens spacing pour les dimensions de composants (voir risques)

Validation

  • Validé le : 19-03-2026
  • Contexte technique : Expo SDK 52+ / React Native / TypeScript — app-alexandrie story 0.1

Implémentation (exemple minimal)

// apps/mobile/src/theme/colors.ts
export const colors = {
  primary: '#2563EB',
  error: '#DC2626',
  // ...
} as const;
export type ColorToken = keyof typeof colors;

// apps/mobile/src/theme/spacing.ts
export const spacing = { xs: 4, sm: 8, md: 12, base: 16, lg: 24 } as const;
export type SpacingToken = keyof typeof spacing;

// apps/mobile/src/theme/index.ts (barrel export)
export * from './colors';
export * from './spacing';
export * from './typography';
export * from './shadows';

Checklist

  • Tous les tokens as const pour inférence exacte
  • Pas de Context React — constantes TypeScript pures
  • Types dérivés (ColorToken = keyof typeof colors) pour l'autocomplétion
  • useFonts dans _layout.tsx avec guard !fontsLoaded
  • Fichiers TTF présents dans assets/fonts/ et documentés dans la story

Pattern : Token typography par usage sémantique (React Native)

Synthèse

  • Objectif : éviter les mauvais usages de tokens typography visuellement proches mais sémantiquement distincts.
  • Contexte : fichier typography.ts dans un design system React Native.
  • Quand l'utiliser : dès que deux tokens partagent la même taille mais un poids différent.
  • Quand l'éviter : jamais — les tokens typography doivent toujours refléter l'usage, pas l'apparence.

Analyse

  • Avantages :
    • prévient les "approximations" de tokens en code review
    • changement de style d'usage spécifique sans régression globale
  • Limites / vigilance :
    • en review : chercher les usages sans fontWeight explicite — c'est souvent le signe que le mauvais token a été choisi

Validation

  • Validé le : 19-03-2026
  • Contexte technique : React Native / TypeScript — app-alexandrie story 0.4

Implémentation

// Bon : nommé par usage sémantique
listItemTitle: { fontSize: 12, fontWeight: '600' },  // titre d'un item de liste
caption: { fontSize: 12, fontWeight: '500' },         // info secondaire, hints

// Mauvais : nommé par apparence
mediumText12: { fontSize: 12, fontWeight: '500' },    // ambigu, réutilisé à tort

Règle : caption (Medium) ≠ listItemTitle (SemiBold) même si la taille est identique. Ne jamais piocher un token "par approximation".


Pattern : Export des styles de composant pour réutilisation partielle (React Native)

Synthèse

  • Objectif : partager les dimensions et formes d'un composant UI vers des éléments custom qui en dérivent, sans dupliquer les valeurs.
  • Contexte : app React Native où des screens construisent des éléments qui doivent être "au gabarit" d'un composant existant.
  • Quand l'utiliser : bouton custom OAuth, container calqué sur un composant de base, etc.
  • Quand l'éviter : si l'écart visuel est intentionnel — dans ce cas, une constante locale est plus claire.

Analyse

  • Avantages :
    • zéro drift silencieux : si les dimensions du composant changent, tous les éléments dérivés suivent
    • tests de styles possibles en dehors du composant
  • Limites / vigilance :
    • à n'utiliser que pour des éléments vraiment dérivés, pas comme contournement de design system

Validation

  • Validé le : 19-03-2026
  • Contexte technique : React Native / StyleSheet — app-alexandrie story 0.3

Implémentation

// Button.tsx
export const buttonStyles = StyleSheet.create({
  base: { borderRadius: 20, height: 57 },
  primary: { backgroundColor: colors.primary },
});
export function Button(...) { ... }

// login.tsx — bouton OAuth au gabarit du Button
import { buttonStyles } from '@/components/ui/Button';
<TouchableOpacity style={[buttonStyles.base, styles.facebookButton]} />

Pattern : Palette light/dark MD3 + hook useThemedColors + dual export

Synthèse

  • Objectif : poser un theming light/dark/system complet sur une codebase qui n'avait qu'une palette unique, sans casser les imports historiques.
  • Contexte : React Native / Expo avec préférence utilisateur light/dark/system.
  • Quand l'utiliser : introduction d'un 2ᵉ thème sur une base existante.
  • Quand l'éviter : thème unique sans besoin de bascule (le hook serait du sucre sans bénéfice).

Analyse

  • Avantages :
    • compat ascendante : export const colors = dark garde les imports import { colors } compilants
    • le hook useThemedColors rend le reste mécanique (le composant ne code jamais le scheme en dur)
    • dual export pour les styles legacy : export const fooStyles = makeStyles(colors) (snapshot dark) reste synchronisé avec la fonction dynamique
  • Limites / vigilance :
    • anti-pattern : palette plate sans typage par mode → force à coder le scheme en dur ou à dupliquer les styleSheets
    • tokens "fixed" partagés light/dark (primaryContainer, primaryFixedDim) pour la continuité du branding ; inverse-primary d'un mode = primary de l'autre
    • shadow : #000000 OK en dark, mais en light un tinted neutre (ex. outline à 8 %), jamais noir pur
    • validation WCAG : README documentant chaque token + ratio mesuré (texte ≥ 4.5:1, UI ≥ 3:1)

Validation

  • Validé le : 29-05-2026
  • Contexte technique : React Native / Expo — app-alexandrie (ux-cleanup-8, ~95 fichiers migrés)

Implémentation

// theme/colors.ts
const dark = { background: '#131316', primary: '#cdbdff' } as const;
const light = { background: '#fbf8fe', primary: '#4c00c9' } as const;
export const colors = dark;        // compat ascendante (imports historiques)
export const colorsLight = light;
export const colorsDark = dark;

// theme/use-themed-colors.ts
export function useThemedColors() {
  return useEffectiveColorScheme() === 'light' ? colorsLight : colorsDark;
}

// composant
const themed = useThemedColors();
const styles = useMemo(() => makeStyles(themed), [themed]);
function makeStyles(c: ReturnType<typeof useThemedColors>) {
  return StyleSheet.create({ container: { backgroundColor: c.background } });
}

// styles exportés (compat tests) — source unique
export const fooStyles = makeStyles(colors); // snapshot dark

Les pièges de migration associés (composants tiers, <Text> sans color, scheme effectif, ThemeProvider) sont documentés dans risques/design-tokens.md#risque-theming-light-dark-pieges-caches.


Pattern : Map sémantique slug → token + icône

Synthèse

  • Objectif : différencier visuellement des entités d'un même type (forums, packs, badges) via une map slug → token résolue au runtime, sans hex en dur.
  • Contexte : entités multiples d'un même type nécessitant un code couleur/icône cohérent light/dark.
  • Quand l'utiliser : ≥ 2 entités à différencier visuellement.
  • Quand l'éviter : entité unique ou différenciation purement textuelle.

Analyse

  • Structure : getEntityBadgeTokens(slug) → { bgColorKey, fgColorKey, icon }, le caller résout via useThemedColors (suit le thème).
  • Règles : (1) toujours des ColorKey de useThemedColors, jamais de hex ; (2) toujours un fallback neutre (un slug futur ne casse pas l'UI) ; (3) icônes outline légères ; (4) tests env node sur map + fallback + distinctness.
  • Avantages : vs if/else inline → 0 duplication ; vs hex map → suit le thème ; vs composant → reste un helper pur testable env node.

Validation

  • Validé le : 29-05-2026
  • Contexte technique : React Native — app-alexandrie (forum-badge.ts, ux-cleanup-15)

Pattern : Migration tokens typo formalisés

Synthèse

  • Objectif : formaliser et migrer les valeurs typographiques en dur (fontSize, fontWeight, fontFamily) vers des tokens, comme le sweep tokens couleur.
  • Contexte : codebase avec ~100 occurrences de valeurs typo en dur sur des dizaines de fichiers.
  • Quand l'utiliser : volumétrie significative de valeurs typo dispersées.
  • Quand l'éviter : poignée d'usages localisés.

Analyse

  • Méthodologie en 4 étapes : (1) audit grep + rapport stats ; (2) étendre typography.ts SANS renommer les tokens existants (rétro-compat) — ajouter des aliases sémantiques ; (3) migration mécanique (perl/sed) + imports, typecheck après chaque batch ; (4) doc README (table sémantique + anti-patterns).
  • Cas dégénérés à laisser en dur : fontSize: 48/64 sur un emoji/hero = taille d'icône, pas de la typo → ne pas créer un token iconLarge (documenter l'exception).
  • Anti-patterns : codemod auto sans review visuel (un 15 → 14 casse un layout) ; renommer un token existant (fontSize.tab utilisé partout → casse 50+ fichiers) ; créer useThemedTypography() si la typo ne varie pas light/dark (sucre sans bénéfice, garder l'import statique).

Validation

  • Validé le : 30-05-2026
  • Contexte technique : React Native — app-alexandrie (ux-cleanup-12, 110 occurrences / 33 fichiers, 0 régression)