Extraction de composants & Design System
1. Utiliser @apply
Extraire des composants CSS réutilisables
2. Personnaliser tailwind.config
Couleurs, fonts, spacing custom
3. Design tokens
Système de design cohérent
4. Animations custom
animate-* et @keyframes
5. Introduction à Radix UI
Composants headless accessibles
@apply : extraction de composants CSS
Quand et comment l'utiliser (et pourquoi ne pas en abuser)
Personnaliser tailwind.config
Couleurs du projet, fonts, spacing custom
Design tokens
Définir un système de design cohérent avec Tailwind
Animations built-in et custom
animate-spin, animate-pulse, animations personnalisées
Introduction à Radix UI
Primitives headless pour composants accessibles
@apply : Extraction de composants
Quand et comment extraire des classes répétées
Vous avez le même bouton à 10 endroits différents...
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Sauvegarder
</button>
...
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Envoyer
</button>
... (x10)
Copier-coller = maintenance difficile. Si on veut changer le padding ?
Extraire les classes dans une classe CSS personnalisée
@layer components {
.btn-primary {
@apply bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded;
}
}
<button class="btn-primary">Sauvegarder</button>
<button class="btn-primary">Envoyer</button>
Une seule classe = maintenance centralisée
Pourquoi @layer ?
Place vos styles dans la couche "components" de Tailwind
Permet de contrôler l'ordre de spécificité
Les 3 couches
base - reset CSS
components - vos classes
utilities - classes utilitaires
/* Tailwind v4 - dans votre fichier CSS */
@layer components {
.btn-primary { @apply ... }
.input-base { @apply ... }
}
Cas valides
À éviter
Règle d'or : @apply uniquement pour les composants TRÈS répétés
Abuser de @apply = revenir au CSS classique
La philosophie Tailwind
En React, on extrait des composants, pas des classes CSS
Au lieu de @apply, créez un composant réutilisable
function
Button
({ children, variant = 'primary' }) {const variants = {
primary: 'bg-blue-500 hover:bg-blue-700 text-white',
secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-800',
};
return (
<button className={`
${variants[variant]} font-bold py-2 px-4 rounded
`}>
{children}
</button>
);
}
Plus flexible, plus maintenable, plus "React"
Personnaliser tailwind.config
LA force de Tailwind pour les projets d'équipe
Plus de tailwind.config.js - tout dans le CSS !
@import
"tailwindcss";
@theme {
--color-brand: #3b82f6;
--font-display: "Inter", sans-serif;
--spacing-18: 4.5rem;
}
v3 (JavaScript)
module.exports = { theme: { ... } }
v4 (CSS)
@theme { --color-*: ... }
Suivre l'échelle 50-950 de Tailwind
@theme {
/* Couleur brand complète */
--color-brand-50: oklch(0.97 0.01 250);
--color-brand-100: oklch(0.94 0.02 250);
--color-brand-200: oklch(0.88 0.04 250);
...
--color-brand-900: oklch(0.35 0.10 250);
--color-brand-950: oklch(0.20 0.08 250);
}
Utilisation : bg-brand-500, text-brand-700
L'échelle 50-950 assure la cohérence avec les couleurs natives
Font families
@theme {
--font-display: "Inter", sans-serif;
--font-mono: "JetBrains Mono", monospace;
}
Usage: font-display
Spacing custom
@theme {
--spacing-18: 4.5rem; /* 72px */
--spacing-128: 32rem;
}
Usage: p-18, m-128
En v3, cette distinction était cruciale. En v4, c'est plus simple !
v3 - Écraser (dangereux)
module.exports = {
theme: {
colors: { blue: '#3b82f6' }
// Remplace TOUTES les couleurs!
}
}
v3 - extend (correct)
module.exports = {
theme: {
extend: {
colors: { brand: '...' }
}
}
}
v4 : @theme ajoute automatiquement, pas besoin de extend !
@import
"tailwindcss";
@theme {
/* Brand colors */
--color-primary-500: oklch(0.6 0.2 250);
--color-primary-700: oklch(0.45 0.18 250);
/* Typography */
--font-sans: "Inter", system-ui, sans-serif;
--font-mono: "JetBrains Mono", monospace;
/* Custom spacing */
--spacing-section: 6rem;
/* Animations */
--animate-fade-in: fade-in 0.5s ease-out;
}
Un fichier, toutes vos personnalisations
Design Tokens
Un système de design cohérent avec Tailwind
Une valeur nommée qui représente une décision de design
--color-primary
#3b82f6
--spacing-md
1rem (16px)
--radius-lg
0.5rem
Les tokens créent un langage commun entre designers et développeurs
Le @theme block est votre source de vérité
@theme {
/* Couleurs sémantiques */
--color-bg-primary: var(--color-slate-50);
--color-text-primary: var(--color-slate-900);
--color-accent: var(--color-brand-500);
/* Spacing tokens */
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 2rem;
}
Usage: bg-bg-primary, text-text-primary
Cohérence visuelle
Même couleur "primary" partout
Maintenance facile
Changer une valeur = tout le site mis à jour
Documentation vivante
Le code EST la documentation
Collaboration design/dev
Figma tokens = CSS tokens
Outils comme Style Dictionary ou Tokens Studio synchronisent Figma et code
Animations Custom
Built-in et animations personnalisées
animate-spin
Pour les loaders
animate-pulse
Pour les états de chargement
animate-bounce
Pour attirer l'attention
animate-ping
Pour les notifications
Définir @keyframes et l'utiliser dans @theme
@keyframes
fade-in
{from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
@theme {
--animate-fade-in: fade-in 0.5s ease-out;
}
<div class="animate-fade-in">
Ce contenu apparaît en douceur
</div>
Duration
duration-150
duration-300
duration-500
duration-1000
Timing
ease-linear
ease-in
ease-out
ease-in-out
Delay
delay-150
delay-300
delay-500
Iteration
animate-once
animate-twice
animate-infinite
<div class="animate-bounce duration-1000 ease-in-out animate-infinite">
@keyframes
shake
{0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
@theme {
--animate-shake: shake 0.3s ease-in-out;
}
// Utilisation pour un input invalide
<input class="border-red-500 animate-shake" />
Feedback visuel instantané pour l'utilisateur
Introduction à Radix UI
Composants headless pour l'accessibilité
Comportement + Accessibilité, SANS style
Radix UI fournit
Vous fournissez
Combinaison parfaite : Radix = fonctionnalité, Tailwind = style
import
{ Dialog } from '@radix-ui/react-dialog';
<Dialog.Root>
<Dialog.Trigger className="btn-primary">
Ouvrir
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="bg-black/50 fixed inset-0" />
<Dialog.Content className="bg-white p-6 rounded-lg shadow-xl fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
<Dialog.Title className="text-xl font-bold">Titre</Dialog.Title>
<p className="text-slate-600 mt-2">Contenu du dialog</p>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
Radix gère l'accessibilité, vous contrôlez 100% du visuel
Dialog
Modales
Dropdown
Menus déroulants
Tabs
Onglets
Tooltip
Infobulles
Popover
Popups
Select
Listes déroulantes
Accordion
Accordéons
Switch
Toggle
Slider
Curseurs
Tous accessibles par défaut, prêts à être stylés avec Tailwind
Utiliser @apply pour TOUT
Ça revient à écrire du CSS classique
Solution : @apply uniquement pour composants très répétés
Couleurs custom sans échelle 50-950
Impossible d'utiliser bg-brand-100 ou text-brand-700
Solution : Définir l'échelle complète ou utiliser les couleurs natives
Confondre extend et écraser (v3)
Perdre toutes les valeurs par défaut
Solution : En v4, @theme ajoute automatiquement
Installer Radix sans comprendre le concept
Attendre des styles prêts à l'emploi
Solution : Radix = comportement, vous = style
@apply
Extraction CSS
Pour composants répétés
@theme
Personnalisation v4
Couleurs, fonts, spacing
Design tokens
Système cohérent
Langage commun
Radix UI
Headless components
Accessibilité native
Préview React : on extraira des composants, pas des classes CSS
Tailwind avancé maîtrisé !
Prochaine étape : Pratiquer avec un vrai projet