Pourquoi Tailwind v4 change la donne
Tailwind v4 pousse les tokens vers le CSS natif via `@theme`. Exit le fichier `tailwind.config.js` bavard — place à des variables CSS réelles, utilisables en JS et en CSS, surchargeables par média, theme, ou contexte.
Avantage immédiat : un dark mode qui ne nécessite plus de `dark:` sur chaque classe. Vous redéfinissez les variables CSS dans un `:root[data-theme="dark"]` et tout le design system bascule proprement.
Performance : Tailwind v4 génère moins de CSS que la v3 pour le même design. Sur la base VALRY LABS, le CSS final est passé de 32 kB à 18 kB gzip (-44 %).
Tokens : la source de vérité
Structure en 3 couches : tokens bruts (couleurs de base, échelles, durées), tokens sémantiques (couleurs de fond, de bordure, d'accent par contexte), composants (variante X du bouton).
Les tokens bruts ne sont jamais utilisés directement dans les composants. On passe toujours par les sémantiques. Ça permet de changer la palette de base sans toucher aux composants.
Convention de nommage sémantique : `--color-bg-primary`, `--color-fg-primary`, `--color-border-strong`, `--color-accent`. Lisible, sans surprise, dark-mode ready.
Stockage : tokens en CSS (`globals.css`), miroirs en TypeScript (`tokens.ts`) pour les usages JS (canvas, charts dynamiques). Les deux fichiers sont générés depuis un unique source `tokens.json` via Style Dictionary.
Primitives : la couche atomique
Primitives = les briques de base réutilisables : Button, Card, Container, Heading, Badge, Overline. Pas de logique métier, uniquement des variants stylistiques.
Nous utilisons shadcn/ui comme base (Radix + Tailwind, copy-paste, pas de dépendance runtime) puis customisons. Ça évite de réinventer la roue sur les composants complexes (Dialog, Popover, Combobox) tout en restant propriétaire du code.
Règle : chaque primitive expose ses variants via `cva` (class-variance-authority). Pas de ternaires dans les `className`, pas de strings concaténées. La composition est typée et outillée par l'IDE.
Accessibilité native : Radix UI garantit le comportement clavier, ARIA, focus trap, etc. On ne réécrit jamais un Combobox ou un Dialog à la main — on habille Radix.
Composites : la couche métier
Composites = assemblages de primitives en unités fonctionnelles : `ServiceCard`, `TestimonialCard`, `HeroSection`, `CTASection`. C'est ici que vit le design signature de la marque.
Un composite ne réinvente jamais un bouton : il consomme `<Button>` et lui passe les bons `variant` / `size`. L'API d'un composite est intentionnellement limitée — pas de fuite de primitives.
Sur VALRY LABS, les composites vivent dans `components/sections/` (les blocs de page complets) ou `components/cards/` (les cartes métier). Chacun a sa démo Storybook.
Accessibilité : non-négociable
WCAG 2.2 AA est la cible minimum. Contrastes validés par `@axe-core/playwright` en CI. Aucune merge si une violation critique est détectée.
Composants : focus visible (`focus-visible:ring-2`), tailles de touch target ≥ 44×44 px, `aria-label` sur les boutons icône-only, `aria-describedby` pour les erreurs de formulaire.
Animations : tout respecte `prefers-reduced-motion`. Les motion components ont un fallback instant. La beauté ne justifie jamais l'exclusion.
Audit régulier : Lighthouse a11y ≥ 95 obligatoire, audit manuel NVDA/VoiceOver à chaque nouvelle feature majeure.