Skip to Content
Claude CodePartie 4 — Design cohérent & anti-dérive4.5 — Atelier : le design system de TaskFlow

Chapitre 4.5 — Atelier : le design system de TaskFlow

⏱️ TL;DR — On pose le design system de TaskFlow en trois gestes concrets : (1) les tokens sémantiques (CSS variables + exposition Tailwind), (2) le Button modèle qui n’utilise que des tokens, (3) les garde-fous (point d’entrée index.ts, règle de lint anti-valeur-arbitraire, ajouts au CLAUDE.md). À la fin, produire un écran cohérent devient le chemin de moindre effort pour l’agent — objectif atteint.

🎯 Objectifs

  • Écrire des tokens sémantiques exploitables par Tailwind.
  • Faire un composant-modèle exemplaire (tokens, variantes, a11y).
  • Installer les garde-fous anti-dérive.
  • Consigner le tout dans le CLAUDE.md.

1. Les tokens

Dans src/app/globals.css, on définit les rôles (sémantiques), adossés à une palette primitive :

:root { /* primitifs */ --clay-400: #eda07f; --clay-500: #d97757; --clay-600: #a8481f; --slate-50: #f8fafc; --slate-500: #64748b; --slate-900: #0f172a; /* sémantiques — CE qu'on utilise dans les composants */ --color-brand: var(--clay-500); --color-brand-strong: var(--clay-600); --color-surface: #ffffff; --color-surface-2: var(--slate-50); --color-text: var(--slate-900); --color-text-muted: var(--slate-500); --color-border: #e2e8f0; --color-danger: #dc2626; --color-success: #16a34a; --radius-card: 14px; --radius-sm: 8px; } @media (prefers-color-scheme: dark) { :root { --color-surface: #14100e; --color-surface-2: #1c1613; --color-text: #f1f5f9; --color-text-muted: #94a3b8; --color-border: #2a211d; --color-brand: var(--clay-400); } }

On expose ces rôles à Tailwind (config), pour avoir bg-brand, text-muted, rounded-card… :

// tailwind.config.js (extrait) export default { theme: { extend: { colors: { brand: 'var(--color-brand)', 'brand-strong': 'var(--color-brand-strong)', surface: 'var(--color-surface)', 'surface-2': 'var(--color-surface-2)', text: 'var(--color-text)', muted: 'var(--color-text-muted)', border: 'var(--color-border)', danger: 'var(--color-danger)', success: 'var(--color-success)', }, borderRadius: { card: 'var(--radius-card)', sm: 'var(--radius-sm)' }, }, }, }

Désormais un composant écrit className="bg-brand text-white rounded-card" — des rôles, jamais des hex.

2. Le Button modèle

Le composant de référence que tout imitera. Tokens, variantes typées, a11y :

// src/components/ui/button.tsx import { type ButtonHTMLAttributes, forwardRef } from 'react' import { clsx } from 'clsx' type Variant = 'primary' | 'ghost' | 'danger' type Size = 'sm' | 'md' interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> { variant?: Variant size?: Size } const variants: Record<Variant, string> = { primary: 'bg-brand text-white hover:bg-brand-strong', ghost: 'bg-transparent text-text hover:bg-surface-2', danger: 'bg-danger text-white hover:opacity-90', } const sizes: Record<Size, string> = { sm: 'h-8 px-3 text-sm', md: 'h-10 px-4 text-sm', } export const Button = forwardRef<HTMLButtonElement, ButtonProps>( ({ variant = 'primary', size = 'md', className, ...props }, ref) => ( <button ref={ref} className={clsx( 'inline-flex items-center justify-center rounded-sm font-medium', 'transition-colors focus-visible:outline-none focus-visible:ring-2', 'focus-visible:ring-brand disabled:opacity-50 disabled:pointer-events-none', variants[variant], sizes[size], className, )} {...props} /> ), ) Button.displayName = 'Button'

Remarque ce qui en fait un modèle : pas de any, variantes en props (pas de composants dupliqués), tokens partout, focus-visible et disabled gérés. Chaque futur composant reprendra ce squelette.

3. Les garde-fous

Point d’entrée unique :

// src/components/ui/index.ts export { Button } from './button' // export { Input } from './input' ← au fur et à mesure

Règle de lint anti-valeur-arbitraire (extrait de config ESLint, via le plugin Tailwind ou une règle no-restricted-syntax) pour bannir bg-[...], p-[13px], hex en dur. L’agent ne peut plus « sortir » du système sans que le lint le refuse.

Ajouts au CLAUDE.md (section design) :

## Design - Couleurs/rayons : tokens sémantiques (`bg-brand`, `text-muted`, `rounded-card`). Interdit : hex en dur, classes arbitraires `[...]`. [imposé par lint] - Composants d'UI : uniquement `@/components/ui`. Chercher avant de créer ; nouvelle primitive = suit `button.tsx` et est ajoutée à `index.ts`. - Variante d'un composant = une prop, jamais un nouveau composant.

Ce qu’on a gagné

TaskFlow a maintenant un système : l’agent hérite des tokens, imite le modèle, réutilise les primitives, et le lint l’empêche de dériver. La cohérence n’est plus une affaire de vigilance — elle est outillée. La passe de cohérence (4.3) servira de filet ; la vérification visuelle (Partie 10, MCP navigateur) fermera l’angle mort du rendu.

🧭 Sur TaskFlow — Le fil rouge a désormais direction (P2), mémoire/conventions (P3) et design system (P4). Il est prêt à recevoir sa première vraie feature — le CRUD des tâches — qu’on construira en pilotant la boucle de travail (Partie 5), puis qu’on sécurisera avec hooks (P7) et revue (P11).

✏️ Exercices

Exercice 1 — Ton mini design system. Sur un projet, pose 8 tokens sémantiques + expose-les à Tailwind + fais UN composant-modèle exemplaire. Vérifie qu’un second composant demandé « comme le modèle » hérite bien des tokens.

✅ Solution

Le test de réussite : le second composant utilise bg-brand/text-muted (des rôles) sans que tu l’aies redit, et ses variantes sont des props. Si ce n’est pas le cas, ton modèle n’était pas assez net, ou la règle « suis le modèle » manque au CLAUDE.md.

Exercice 2 — Le lint qui protège. Ajoute la règle interdisant les valeurs arbitraires Tailwind. Lance-la, corrige les violations vers des tokens (ou ajoute un token si un vrai besoin manque). Combien de dérives éliminées ?

✅ Solution

Chaque violation corrigée est une dérive supprimée et rendue impossible à recréer (le lint la refusera désormais). C’est l’illustration de la couche « imposer » (3.3) : une fois en place, elle protège sans effort continu de ta part.

🧠 Quiz de révision

1. Pourquoi exposer les tokens comme classes Tailwind (bg-brand) ?

Pour que l’agent écrive des rôles (bg-brand) au lieu de valeurs (bg-[#d97757]), naturellement, dans son flux habituel. Le token devient le chemin de moindre effort.

2. Qu’est-ce qui fait de button.tsx un bon « modèle » ?

Pas de any, variantes en props (pas de doublons), tokens partout, états focus-visible/disabled gérés. Un squelette exemplaire que les futurs composants reprennent.

3. Rôle de la règle de lint anti-valeur-arbitraire ?

Empêcher mécaniquement l’agent (et toi) de « sortir » du système avec des bg-[...] / hex en dur. C’est la couche imposer : elle garantit la règle sans vigilance continue.

4. Où consigne-t-on la règle « variante = une prop » ?

Dans la section Design du CLAUDE.md, en plus d’être montrée par le Button modèle. Énoncer + montrer, pour qu’elle tienne.

5. Après cette partie, sur quoi repose la cohérence de TaskFlow ?

Sur un système outillé (tokens hérités, modèle imité, primitives réutilisées, lint qui bloque la dérive), pas sur la vigilance permanente. La passe de cohérence et la vérif visuelle servent de filets.


Fin de la Partie 4. TaskFlow a un design system. On passe au cœur du quotidien : Partie 5 — Faire coder sans déraper.