ITSkillsCenter
Blog

Comprendre les nouveautés Tailwind v4

8 min de lecture

Ce que vous saurez faire à la fin

  1. Installer Tailwind CSS v4 avec son nouveau moteur Oxide écrit en Rust, 5 à 10 fois plus rapide que la v3.
  2. Configurer un design system cohérent (couleurs, typographie, espacements) en CSS pur, sans tailwind.config.js.
  3. Construire des composants réutilisables (boutons, cards, formulaires) adaptés à une PME sénégalaise avec mode sombre.
  4. Optimiser le CSS de production en passant de 3 Mo à 12 Ko grâce au tree-shaking automatique.
  5. Intégrer Tailwind v4 dans Next.js 15, Vite, Astro ou WordPress sans casser l’existant.

Durée : 3h30. Pré-requis : HTML/CSS de base, npm, un projet web ou la volonté d’en créer un. VS Code recommandé avec extension Tailwind CSS IntelliSense. Coût : 0 FCFA.

Étape 1 — Comprendre les nouveautés Tailwind v4

Tailwind v4, sortie en début 2025 et désormais stable, marque la plus grande refonte depuis la création du framework. Trois changements majeurs : moteur Oxide en Rust (10x plus rapide), configuration en CSS pur (fini le JavaScript), et détection automatique des classes (plus besoin de content dans la config).

Performance benchmarks (projet 200 composants) :
Tailwind v3 (JIT)  : 380 ms par build
Tailwind v4 (Oxide) : 38 ms par build (10x plus rapide)

Bundle CSS de production :
v3 avec purge : 18 Ko gzip
v4 (auto)     : 11 Ko gzip (tree-shaking plus agressif)

Nouveautés syntaxe :
- @theme dans le CSS au lieu de tailwind.config.js
- Variables CSS natives (var(--color-emerald-500))
- @utility pour créer ses propres classes
- Container queries natives

Étape 2 — Installation dans un projet Vite

npm create vite@latest mon-site -- --template vanilla
cd mon-site
npm install
npm install -D tailwindcss @tailwindcss/vite
// vite.config.js
import { defineConfig } from 'vite';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  plugins: [tailwindcss()]
});
/* src/style.css */
@import "tailwindcss";

C’est tout. Plus de tailwind.config.js, plus de postcss.config.js. Une seule ligne d’import et le moteur Oxide scanne automatiquement vos fichiers HTML, JSX, Vue, Svelte, Astro pour générer uniquement les classes utilisées.

Étape 3 — Configuration via @theme

/* src/style.css */
@import "tailwindcss";

@theme {
  /* Palette de marque PME Sénégal */
  --color-marque-50: #f0fdf4;
  --color-marque-500: #00853f; /* vert drapeau Sénégal */
  --color-marque-600: #006b32;
  --color-marque-900: #052e16;

  --color-accent-500: #fdef42; /* jaune drapeau Sénégal */
  --color-accent-700: #c4b836;

  /* Typographie locale-friendly */
  --font-sans: "Inter", "Noto Sans", system-ui, sans-serif;
  --font-display: "Outfit", "Inter", sans-serif;

  /* Breakpoints adaptés mobile-first africain */
  --breakpoint-xs: 360px;  /* Tecno, Itel petits écrans */
  --breakpoint-sm: 640px;
  --breakpoint-md: 768px;
  --breakpoint-lg: 1024px;
  --breakpoint-xl: 1280px;

  /* Rayons de bordure */
  --radius-pill: 9999px;
  --radius-card: 1rem;
}

Toutes les valeurs définies dans @theme deviennent automatiquement utilisables comme classes : bg-marque-500, text-accent-700, font-display, max-xs, rounded-card.

Étape 4 — Premier composant : carte produit

<article class="rounded-card bg-white shadow-md transition hover:shadow-xl
                dark:bg-zinc-900 dark:shadow-zinc-800/50">
  <img
    src="/produits/riz-tilda-5kg.webp"
    alt="Riz Tilda 5 kg"
    class="aspect-square w-full rounded-t-card object-cover"
    loading="lazy"
  />
  <div class="p-4 space-y-2">
    <h3 class="font-display text-lg font-semibold text-zinc-900
              dark:text-zinc-100">
      Riz Tilda Basmati 5 kg
    </h3>
    <p class="text-sm text-zinc-600 dark:text-zinc-400">
      Riz long grain importé d'Inde, parfumé.
    </p>
    <div class="flex items-center justify-between pt-2">
      <span class="text-xl font-bold text-marque-600">12 500 FCFA</span>
      <button class="rounded-pill bg-marque-500 px-4 py-2 text-sm
                    font-medium text-white transition
                    hover:bg-marque-600 active:scale-95">
        Ajouter
      </button>
    </div>
  </div>
</article>

Étape 5 — Mode sombre automatique

/* Activer la stratégie 'class' pour basculer manuellement */
@import "tailwindcss";

@variant dark (&:where(.dark, .dark *));
<!-- Bouton bascule mode sombre -->
<button onclick="document.documentElement.classList.toggle('dark')"
        class="fixed top-4 right-4 rounded-full bg-zinc-200
               p-2 dark:bg-zinc-800">
  Basculer
</button>

<!-- Sauvegarder le choix -->
<script>
  const pref = localStorage.getItem('theme');
  if (pref === 'dark' || (!pref &&
      window.matchMedia('(prefers-color-scheme: dark)').matches)) {
    document.documentElement.classList.add('dark');
  }
</script>

Étape 6 — Variants modernes : has, not, peer

<!-- Style conditionnel basé sur les enfants (has) -->
<form class="rounded-lg border p-4 has-[input:invalid]:border-red-500
             has-[input:valid]:border-green-500">
  <input type="email" required class="w-full rounded border px-3 py-2" />
</form>

<!-- Inverser une condition (not) -->
<div class="not-hover:opacity-70 hover:opacity-100 transition">
  Survolez pour rendre lisible
</div>

<!-- Peer : réagir à l'état d'un sibling -->
<label class="block">
  <input type="checkbox" class="peer sr-only" />
  <span class="block rounded border-2 border-zinc-300 p-4
              peer-checked:border-marque-500 peer-checked:bg-marque-50
              cursor-pointer transition">
    Livraison Wave (gratuite)
  </span>
</label>

Étape 7 — Container queries natives

<!-- Le composant s'adapte à la largeur de SON parent, pas du viewport -->
<div class="@container">
  <div class="grid grid-cols-1 gap-4
              @md:grid-cols-2 @lg:grid-cols-3">
    <article>Produit 1</article>
    <article>Produit 2</article>
    <article>Produit 3</article>
  </div>
</div>

Avant : un composant carte ne pouvait pas savoir s’il était dans une sidebar étroite ou en pleine largeur. Avec @container, il s’adapte automatiquement. Indispensable pour des composants réutilisables dans plusieurs contextes (dashboard, page produit, modal).

Étape 8 — Créer ses propres utilities

@import "tailwindcss";

/* Composer une classe métier */
@utility btn-wave {
  @apply rounded-pill bg-marque-500 px-6 py-3 text-white
         font-medium transition hover:bg-marque-600
         active:scale-95 disabled:opacity-50;
}

@utility text-balance {
  text-wrap: balance;
}

/* Utility avec valeurs dynamiques */
@utility tab-* {
  tab-size: --value(integer);
}
/* Utilisation : tab-2, tab-4, tab-8 */

Étape 9 — Formulaires accessibles

<form class="mx-auto max-w-md space-y-4 p-6">
  <div>
    <label for="nom" class="block text-sm font-medium text-zinc-700
                            dark:text-zinc-300">
      Nom complet
    </label>
    <input
      id="nom"
      type="text"
      required
      class="mt-1 block w-full rounded-lg border-zinc-300 shadow-sm
             focus:border-marque-500 focus:ring-2 focus:ring-marque-500/20
             dark:bg-zinc-800 dark:border-zinc-700 dark:text-white"
    />
  </div>

  <div>
    <label for="tel" class="block text-sm font-medium text-zinc-700
                            dark:text-zinc-300">
      Téléphone Wave
    </label>
    <input
      id="tel"
      type="tel"
      pattern="\+221[0-9]{9}"
      placeholder="+221 77 123 45 67"
      class="mt-1 block w-full rounded-lg border-zinc-300 shadow-sm
             invalid:border-red-300 invalid:focus:ring-red-200
             dark:bg-zinc-800 dark:border-zinc-700"
    />
  </div>

  <button type="submit" class="btn-wave w-full">
    Valider la commande
  </button>
</form>

Étape 10 — Animations fluides

@import "tailwindcss";

@theme {
  --animate-pulse-soft: pulse-soft 2s ease-in-out infinite;
  --animate-slide-up: slide-up 300ms ease-out;
}

@keyframes pulse-soft {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.7; }
}

@keyframes slide-up {
  from { transform: translateY(20px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}
<div class="animate-slide-up rounded-card bg-white p-4">
  Notification : Votre commande est confirmée
</div>

<span class="animate-pulse-soft rounded-pill bg-emerald-100
            px-3 py-1 text-emerald-700">
  En direct
</span>

Étape 11 — Intégration dans Next.js 15

cd mon-app-next
npm install -D tailwindcss @tailwindcss/postcss
// postcss.config.mjs
export default {
  plugins: { '@tailwindcss/postcss': {} }
};
/* app/globals.css */
@import "tailwindcss";

@theme {
  --color-marque-500: #00853f;
}
// app/layout.tsx
import './globals.css';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="fr-SN">
      <body className="bg-white text-zinc-900 antialiased">
        {children}
      </body>
    </html>
  );
}

Étape 12 — Intégration WordPress

cd wp-content/themes/mon-theme
npm init -y
npm install -D tailwindcss @tailwindcss/cli
/* assets/css/source.css */
@import "tailwindcss";

@source "../../**/*.php";
@source "../../**/*.html";

@theme {
  --color-marque-500: #00853f;
}
// package.json scripts
{
  "scripts": {
    "build:css": "tailwindcss -i assets/css/source.css -o assets/css/style.css --minify",
    "watch:css": "tailwindcss -i assets/css/source.css -o assets/css/style.css --watch"
  }
}
<?php // functions.php
function enqueue_tailwind() {
  wp_enqueue_style('tailwind',
    get_template_directory_uri() . '/assets/css/style.css',
    [], filemtime(get_template_directory() . '/assets/css/style.css'));
}
add_action('wp_enqueue_scripts', 'enqueue_tailwind');

Étape 13 — Optimisation de production

npm run build
# Inspecter la taille du CSS final
ls -lh dist/assets/*.css

# Audit avec PurgeCSS-like (déjà fait par Tailwind)
# Vérifier qu'aucune classe non utilisée n'est présente

# Compression Brotli côté serveur
# Sur Nginx :
# brotli on;
# brotli_types text/css application/javascript;

Étape 14 — Plugin VS Code et workflow

Extensions VS Code essentielles :
1. Tailwind CSS IntelliSense (autocomplétion + preview couleurs)
2. Headwind (tri automatique des classes)
3. PostCSS Language Support

Réglage settings.json :
{
  "tailwindCSS.experimental.classRegex": [
    ["clsx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"],
    ["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
  ],
  "editor.quickSuggestions": { "strings": "on" }
}

Erreurs fréquentes

  • Classes non détectées : Tailwind v4 scanne par défaut tout sauf node_modules et .git. Pour inclure d’autres dossiers, utilisez @source « ../app/**/*.{html,js} ».
  • Configuration JS oubliée : ne créez plus de tailwind.config.js. Tout se passe dans le CSS via @theme et @utility.
  • Classes dynamiques tronquées : bg-{couleur} ne marche pas (Tailwind ne peut pas deviner). Utilisez un mapping JS : const colors = { rouge: ‘bg-red-500’ }.
  • Mode sombre absent : oubli du @variant dark. Sans cette ligne, dark: ne fonctionne pas en stratégie class.
  • CSS de 3 Mo en dev : normal car Tailwind charge tout en mode dev. La build production fait du tree-shaking et tombe à 10-20 Ko.

Checklist de validation

  • Tailwind v4 installé via @tailwindcss/vite ou @tailwindcss/postcss
  • Une seule ligne @import « tailwindcss » dans le fichier CSS principal
  • Bloc @theme défini avec au moins 3 couleurs personnalisées
  • Au moins 2 composants construits avec classes Tailwind (carte, formulaire)
  • Mode sombre configuré et bouton de bascule fonctionnel
  • Au moins 1 utility custom créée via @utility
  • Container query utilisée sur un composant réutilisable
  • Build production exécuté et taille CSS vérifiée (idéalement moins de 25 Ko)
  • VS Code Tailwind IntelliSense installé et autocomplétion fonctionnelle
  • Test mobile sur smartphone réel ou Chrome DevTools (responsive OK)
  • Test Lighthouse : performances supérieures à 90 sur mobile
  • Documentation interne avec la palette et les utilities personnalisées
Besoin d'un site web ?

Confiez-nous la Création de Votre Site Web

Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.

À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité