Tailwind CSS
This content is not available in your language yet.
🎨 Qu’est-ce que Tailwind CSS ?
Section intitulée « 🎨 Qu’est-ce que Tailwind CSS ? »Tailwind CSS est un framework CSS utility-first qui révolutionne la façon de styliser les interfaces web. Au lieu d’écrire du CSS personnalisé, vous composez vos designs directement en HTML avec des classes utilitaires.
🆚 Approche classique vs Tailwind
Section intitulée « 🆚 Approche classique vs Tailwind »CSS classique :
<!-- HTML --><button class="btn btn-primary">Ajouter au panier</button>
<!-- CSS séparé -->.btn { padding: 0.5rem 1rem; border-radius: 0.375rem; font-weight: 600;}.btn-primary { background-color: #0073e6; color: white;}.btn-primary:hover { background-color: #005bb5;}Avec Tailwind :
<!-- Tout dans le HTML --><button class="px-4 py-2 rounded-md font-semibold bg-blue-600 text-white hover:bg-blue-700"> Ajouter au panier</button>✅ Avantages pour le projet e-commerce
Section intitulée « ✅ Avantages pour le projet e-commerce »| Avantage | Impact sur TailStore |
|---|---|
| Développement rapide | Prototyper des composants produit en minutes |
| Design système intégré | Cohérence couleurs/espacements automatique |
| Responsive natif | Mobile-first avec préfixes sm:, md:, lg: |
| Purge CSS | Fichier final < 10 KB (vs 200 KB avec Bootstrap) |
| Pas de naming | Fini les débats sur .product-card vs .item-card |
| Dark mode | Support natif avec dark: |
| Performance | Aucun CSS inutilisé chargé |
🆕 Tailwind v4 - Nouveautés majeures
Section intitulée « 🆕 Tailwind v4 - Nouveautés majeures »🏗️ Architecture Tailwind + Drupal
Section intitulée « 🏗️ Architecture Tailwind + Drupal »┌─────────────────────────────────────────┐│ Templates Twig (.html.twig) ││ Classes Tailwind dans le HTML │└──────────────┬──────────────────────────┘ │ Vite watch ↓┌─────────────────────────────────────────┐│ assets/style.css ││ @import "tailwindcss" ││ @theme { custom vars } ││ @layer components { .btn } │└──────────────┬──────────────────────────┘ │ npm run build ↓┌─────────────────────────────────────────┐│ dist/assets/main.css (minified) ││ Seulement les classes utilisées │└──────────────┬──────────────────────────┘ │ Déclaré dans .libraries.yml ↓┌─────────────────────────────────────────┐│ Chargé dans <head> par Drupal │└─────────────────────────────────────────┘🎯 Cas d’usage dans TailStore
Section intitulée « 🎯 Cas d’usage dans TailStore »- 🛒 Grilles de produits :
grid grid-cols-4 gap-6 - 🏷️ Badges promo :
badge badge-danger(-30%) - 🔍 Filtres : Formulaires stylés avec
form-input - 📱 Navigation : Menu responsive avec breakpoints
- 🖼️ Galeries images : Lightbox avec transitions
- 💳 Checkout : Formulaire multi-étapes
🆕 Approche Tailwind v4 : CSS natif au lieu de @apply
Section intitulée « 🆕 Approche Tailwind v4 : CSS natif au lieu de @apply »❌ Tailwind v3 (ancienne approche) :
.btn { @apply px-4 py-2 bg-blue-600 text-white rounded;}✅ Tailwind v4 (nouvelle approche) :
.btn { padding-inline: --spacing(4); padding-block: --spacing(2); background-color: var(--color-blue-600); color: var(--color-white); border-radius: var(--radius);}Avantages :
- 🚀 Plus rapide (pas de transformation @apply)
- 🎨 Accès direct aux variables CSS
- 🔧 Meilleure intégration avec les outils CSS
- 📦 CSS plus petit et optimisé
🚀 Installation
Section intitulée « 🚀 Installation »1. Initialiser npm dans le thème
Section intitulée « 1. Initialiser npm dans le thème »cd themes/custom/tailstorenpm init -y2. Installer Tailwind CSS v4
Section intitulée « 2. Installer Tailwind CSS v4 »npm install -D vite @tailwindcss/vite tailwindcss3. Configurer Vite
Section intitulée « 3. Configurer Vite »Créez vite.config.js :
import { defineConfig } from 'vite'import tailwindcss from '@tailwindcss/vite'
export default defineConfig({ plugins: [ tailwindcss(), ], build: { outDir: 'dist', rollupOptions: { input: { main: 'assets/main.js', }, output: { entryFileNames: 'assets/[name].js', chunkFileNames: 'assets/[name].js', assetFileNames: 'assets/[name].[ext]', }, }, },})4. Créer le fichier CSS source
Section intitulée « 4. Créer le fichier CSS source »Créez assets/style.css :
/* assets/style.css */
/* Import Tailwind */@import "tailwindcss";
/* Personnalisation du thème */@theme { /* Couleurs */ --color-primary: #0073e6; --color-primary-dark: #005bb5; --color-secondary: #6c757d; --color-success: #28a745; --color-warning: #ffc107; --color-danger: #dc3545; --color-dark: #1a1a2e; --color-light: #f8f9fa;
/* Fonts */ --font-sans: 'Inter', system-ui, sans-serif;
/* Spacing custom */ --spacing-18: 4.5rem; --spacing-128: 32rem;
/* Border radius */ --radius-btn: 0.375rem; --radius-card: 0.5rem;}
/* Composants personnalisés avec CSS natif (Tailwind v4) */@layer components { /* Boutons */ .btn { display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem; padding-inline: --spacing(4); padding-block: --spacing(2); font-weight: var(--font-weight-semibold); border-radius: var(--radius-btn); transition: all 0.2s; }
.btn-primary { background-color: var(--color-primary); color: var(--color-white); &:hover { @media (hover: hover) { background-color: var(--color-primary-dark); } } }
.btn-secondary { background-color: var(--color-secondary); color: var(--color-white); &:hover { @media (hover: hover) { background-color: color-mix(in srgb, var(--color-secondary) 90%, black); } } }
.btn-outline { border: 1px solid currentColor; background-color: transparent; &:hover { @media (hover: hover) { background-color: var(--color-primary); color: var(--color-white); border-color: var(--color-primary); } } }
/* Cards */ .card { background-color: var(--color-white); border-radius: var(--radius-card); box-shadow: var(--shadow-md); overflow: hidden; }
.card-body { padding: --spacing(4); }
/* Badges */ .badge { display: inline-flex; align-items: center; padding-inline: --spacing(2); padding-block: --spacing(0.5); font-size: var(--text-xs); font-weight: var(--font-weight-medium); border-radius: calc(infinity * 1px); }
.badge-primary { background-color: color-mix(in srgb, var(--color-primary) 10%, transparent); color: var(--color-primary); }
.badge-danger { background-color: color-mix(in srgb, var(--color-danger) 10%, transparent); color: var(--color-danger); }
.badge-success { background-color: color-mix(in srgb, var(--color-success) 10%, transparent); color: var(--color-success); }
/* Formulaires */ .form-input { width: 100%; padding-inline: --spacing(3); padding-block: --spacing(2); border: 1px solid var(--color-gray-300); border-radius: var(--radius-btn); outline: none; transition: colors 0.2s; &:focus { border-color: var(--color-primary); box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-primary) 20%, transparent); } }
.form-label { display: block; font-size: var(--text-sm); font-weight: var(--font-weight-medium); color: var(--color-gray-700); margin-bottom: --spacing(1); }
/* Container */ .container { margin-inline: auto; padding-inline: --spacing(4); max-width: 1280px; @media (width >= 640px) { padding-inline: --spacing(6); } @media (width >= 1024px) { padding-inline: --spacing(8); } }}
/* Utilitaires personnalisés */@layer utilities { .text-balance { text-wrap: balance; }
.line-clamp-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }}4. Configurer le build
Section intitulée « 4. Configurer le build »Créez package.json avec les scripts :
{ "name": "tailstore-theme", "version": "1.0.0", "type": "module", "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, "devDependencies": { "@tailwindcss/vite": "^4.0.0", "tailwindcss": "^4.0.0", "vite": "^5.0.0" }}5. Créer le point d’entrée JavaScript
Section intitulée « 5. Créer le point d’entrée JavaScript »Créez assets/main.js :
// assets/main.jsimport './style.css'
// Votre code JavaScript iciconsole.log('TailStore theme loaded!')5. Builder le CSS
Section intitulée « 5. Builder le CSS »# Démarrer le serveur de développementnpm run dev
# Build productionnpm run build6. Déclarer dans libraries.yml
Section intitulée « 6. Déclarer dans libraries.yml »# tailstore.libraries.ymlglobal: version: 1.0 css: theme: dist/assets/main-*.css: { minified: true } dependencies: - core/drupal🎯 Exercices pratiques
Section intitulée « 🎯 Exercices pratiques »Exercice 1 : Installer et tester Tailwind
Section intitulée « Exercice 1 : Installer et tester Tailwind »Objectif : Configurer Tailwind v4 et vérifier que les classes fonctionnent.
-
Suivez toutes les étapes d’installation ci-dessus
-
Créez un template de test
test-tailwind.html.twig:Fenêtre de terminal touch themes/custom/tailstore/templates/test-tailwind.html.twig -
Ajoutez du HTML avec des classes Tailwind :
<div class="container mx-auto p-8"><h1 class="text-4xl font-bold text-blue-600 mb-4">Tailwind fonctionne ! 🎉</h1><div class="grid grid-cols-3 gap-4"><div class="bg-red-500 text-white p-4 rounded-lg">Rouge</div><div class="bg-green-500 text-white p-4 rounded-lg">Vert</div><div class="bg-blue-500 text-white p-4 rounded-lg">Bleu</div></div></div> -
Lancez le build :
Fenêtre de terminal cd themes/custom/tailstorenpm run dev -
Videz le cache Drupal :
Fenêtre de terminal drush cr -
Visitez une page et inspectez (F12) : les classes doivent être appliquées
Validation : ✅ Le texte est bleu, les 3 boîtes colorées sont visibles
Exercice 2 : Créer un composant bouton
Section intitulée « Exercice 2 : Créer un composant bouton »Objectif : Utiliser @layer components pour créer des boutons réutilisables.
-
Dans
assets/style.css, ajoutez dans@layer components:@layer components {.btn {display: inline-flex;align-items: center;justify-content: center;gap: 0.5rem;padding-inline: --spacing(6);padding-block: --spacing(3);font-weight: var(--font-weight-semibold);border-radius: var(--radius-lg);transition: all 0.2s;outline: none;&:focus {box-shadow: 0 0 0 2px var(--color-white), 0 0 0 4px currentColor;}}.btn-primary {background-color: var(--color-blue-600);color: var(--color-white);&:hover {@media (hover: hover) {background-color: var(--color-blue-700);}}}.btn-secondary {background-color: var(--color-gray-600);color: var(--color-white);&:hover {@media (hover: hover) {background-color: var(--color-gray-700);}}}.btn-outline {border: 2px solid var(--color-blue-600);color: var(--color-blue-600);background-color: transparent;&:hover {@media (hover: hover) {background-color: var(--color-blue-600);color: var(--color-white);}}}.btn-lg {padding-inline: --spacing(8);padding-block: --spacing(4);font-size: var(--text-lg);}.btn-sm {padding-inline: --spacing(3);padding-block: --spacing(1.5);font-size: var(--text-sm);}} -
Testez dans un template :
<div class="flex gap-4 p-8"><button class="btn btn-primary">Primary</button><button class="btn btn-secondary">Secondary</button><button class="btn btn-outline">Outline</button><button class="btn btn-primary btn-lg">Large</button><button class="btn btn-primary btn-sm">Small</button></div> -
Rebuilder le CSS :
Fenêtre de terminal npm run builddrush cr
Validation : ✅ 5 boutons avec styles différents, hover/focus fonctionnent
Exercice 3 : Grille de produits responsive
Section intitulée « Exercice 3 : Grille de produits responsive »Objectif : Créer une grille qui s’adapte à la taille d’écran.
-
Créez un template Views
views-view-unformatted--products.html.twig:<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">{% for row in rows %}{{ row.content }}{% endfor %}</div> -
Testez la responsivité :
- Mobile (< 640px) : 1 colonne
- Tablette (640px+) : 2 colonnes
- Desktop (1024px+) : 3 colonnes
- Large (1280px+) : 4 colonnes
-
Ajoutez des espacements adaptés :
<div class="container mx-auto px-4 sm:px-6 lg:px-8 py-8 lg:py-12"><div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 lg:gap-6"><!-- produits --></div></div> -
Testez en redimensionnant la fenêtre du navigateur
Validation : ✅ La grille change de colonnes selon la taille d’écran
Exercice 4 : Personnaliser le thème avec @theme
Section intitulée « Exercice 4 : Personnaliser le thème avec @theme »Objectif : Adapter les couleurs Tailwind aux couleurs de la marque.
-
Dans
assets/style.css, modifiez@theme:@theme {/* Couleurs de la marque TailStore */--color-brand-primary: #ff6b6b;--color-brand-secondary: #4ecdc4;--color-brand-accent: #ffe66d;--color-brand-dark: #2d3436;--color-brand-light: #f8f9fa;/* Remplacer les couleurs Tailwind par défaut */--color-primary: var(--color-brand-primary);--color-secondary: var(--color-brand-secondary);} -
Utilisez ces couleurs :
<button class="bg-primary text-white px-6 py-3 rounded-lg hover:bg-primary/90">Acheter maintenant</button><div class="bg-secondary/10 border border-secondary text-secondary p-4 rounded">Livraison gratuite dès 50€</div> -
Testez avec différentes opacités :
<div class="bg-primary/10">10%</div><div class="bg-primary/50">50%</div><div class="bg-primary/90">90%</div> -
Rebuilder et vérifier
Validation : ✅ Les couleurs personnalisées sont appliquées partout
🧠 Bonnes pratiques essentielles
Section intitulée « 🧠 Bonnes pratiques essentielles »⚡ Performance
Section intitulée « ⚡ Performance »1. Purge automatique - Vérifier la configuration
Tailwind v4 purge automatiquement, mais vérifiez que tous vos templates sont scannés :
// vite.config.jsexport default defineConfig({ plugins: [ tailwindcss({ // Spécifier où chercher les classes content: [ './templates/**/*.twig', './templates/**/*.html.twig', './src/**/*.php', ], }), ],})2. Éviter les classes dynamiques
{# ❌ Mauvais - Classe générée dynamiquement #}<div class="text-{{ color }}"> {# Ne fonctionnera pas avec la purge #}
{# ✅ Bon - Mapper vers des classes complètes #}{% set color_classes = { 'red': 'text-red-600', 'blue': 'text-blue-600', 'green': 'text-green-600',} %}<div class="{{ color_classes[color] }}">3. Utiliser les composants pour les styles complexes
/* Au lieu de répéter dans chaque template */@layer components { .product-card { background-color: var(--color-white); border-radius: var(--radius-lg); box-shadow: var(--shadow-md); overflow: hidden; transition: all 0.2s; &:hover { @media (hover: hover) { transform: translateY(-0.25rem); box-shadow: var(--shadow-xl); } } }
.product-card-image { aspect-ratio: 1; overflow: hidden; }
.product-card-body { padding: --spacing(4); display: flex; flex-direction: column; gap: 0.5rem; }}4. Optimiser les builds
# Développement - rebuild rapidenpm run dev
# Production - minification maximalenpm run build
# Vérifier la taille finalels -lh dist/assets/*.css# Attendu : < 15 KB après gzip🎨 Conventions de code
Section intitulée « 🎨 Conventions de code »1. Ordre des classes (recommandé)
Utilisez Prettier avec le plugin Tailwind pour trier automatiquement :
npm install -D prettier prettier-plugin-tailwindcss// .prettierrc{ "plugins": ["prettier-plugin-tailwindcss"]}Ordre logique : Layout → Spacing → Sizing → Typography → Colors → Effects
<button class=" flex items-center justify-center {# Layout #} px-6 py-3 gap-2 {# Spacing #} w-full {# Sizing #} text-lg font-semibold {# Typography #} bg-blue-600 text-white {# Colors #} rounded-lg shadow-md {# Effects #} hover:bg-blue-700 hover:shadow-lg {# States #} transition-all duration-200 {# Transitions #}"> Ajouter au panier</button>2. Composants vs Utilitaires
/* Créer un composant SI répété 3+ fois */@layer components { .badge { @apply inline-flex items-center px-2 py-0.5; @apply text-xs font-medium rounded-full; }
.badge-primary { @apply bg-blue-100 text-blue-800; }}{# Utiliser directement les utilitaires pour des cas uniques #}<div class="flex items-center justify-between p-4 bg-gray-50 rounded"> <!-- Contenu unique --></div>3. Responsive - Mobile First
{# ❌ Mauvais - Desktop first #}<div class="grid-cols-4 md:grid-cols-2 sm:grid-cols-1">
{# ✅ Bon - Mobile first #}<div class="grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4"> {# Mobile: 1 col, Tablet: 2 col, Desktop: 3 col, Large: 4 col #}</div>♿ Accessibilité
Section intitulée « ♿ Accessibilité »1. Focus visible
<button class=" btn btn-primary focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:outline-none"> Action</button>2. Contraste des couleurs
/* Vérifier le contraste (WCAG AA = 4.5:1) */@layer components { .btn-primary { @apply bg-blue-600 text-white; /* Contraste: 8.6:1 ✅ */ }
.text-muted { @apply text-gray-600; /* Sur fond blanc: 5.7:1 ✅ */ }}3. Textes alternatifs et aria
{# Icônes avec texte caché #}<button class="btn btn-primary"> <svg class="w-5 h-5" aria-hidden="true">...</svg> <span class="sr-only">Ajouter au panier</span></button>
{# sr-only = screen reader only #}/* Classe sr-only intégrée dans Tailwind */.sr-only { position: absolute; width: 1px; height: 1px; clip: rect(0, 0, 0, 0);}🔄 Maintenance
Section intitulée « 🔄 Maintenance »1. Documenter les composants personnalisés
/** * Product Card Component * * Usage: * <article class="product-card"> * <img class="product-card-image" /> * <div class="product-card-body">...</div> * </article> * * Variants: .product-card-featured, .product-card-compact */@layer components { .product-card { /* ... */ }}2. Versionner les couleurs
@theme { /* Version 1.0 - Couleurs initiales */ --color-primary-v1: #0073e6;
/* Version 2.0 - Nouveau branding (2026-01) */ --color-primary: #ff6b6b; --color-primary-dark: #e85a5a; --color-primary-light: #ff8787;}📄 Intégration avec les templates
Section intitulée « 📄 Intégration avec les templates »Template page.html.twig avec Tailwind
Section intitulée « Template page.html.twig avec Tailwind »{#/** * @file * TailStore page template with Tailwind CSS. */#}<div class="min-h-screen flex flex-col bg-light">
{# Header #} <header class="bg-white shadow-sm sticky top-0 z-50"> <div class="container"> <div class="flex items-center justify-between h-16">
{# Logo #} <div class="flex-shrink-0"> {{ page.header }} </div>
{# Navigation principale #} <nav class="hidden md:flex items-center space-x-8"> {{ page.primary_menu }} </nav>
{# Actions header #} <div class="flex items-center space-x-4"> {{ page.secondary_menu }}
{# Mobile menu button #} <button type="button" class="md:hidden p-2 text-gray-600 hover:text-primary" x-data @click="$dispatch('toggle-mobile-menu')" > <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/> </svg> </button> </div>
</div> </div> </header>
{# Hero/Highlighted #} {% if page.highlighted %} <section class="relative"> {{ page.highlighted }} </section> {% endif %}
{# Breadcrumb #} {% if page.breadcrumb %} <nav class="bg-gray-50 border-b" aria-label="Breadcrumb"> <div class="container py-3"> {{ page.breadcrumb }} </div> </nav> {% endif %}
{# Main content #} <main class="flex-grow py-8" id="main-content"> <div class="container">
{{ page.help }}
<div class="{% if page.sidebar %}grid lg:grid-cols-4 gap-8{% endif %}">
{# Content #} <div class="{% if page.sidebar %}lg:col-span-3{% endif %}"> {{ page.content }} </div>
{# Sidebar #} {% if page.sidebar %} <aside class="lg:col-span-1 space-y-6"> {{ page.sidebar }} </aside> {% endif %}
</div>
{% if page.content_below %} <div class="mt-12"> {{ page.content_below }} </div> {% endif %}
</div> </main>
{# Footer #} <footer class="bg-dark text-white"> {% if page.footer_top %} <div class="container py-12"> <div class="grid md:grid-cols-4 gap-8"> {{ page.footer_top }} </div> </div> {% endif %}
{% if page.footer_bottom %} <div class="border-t border-white/10"> <div class="container py-6"> <div class="flex flex-col md:flex-row justify-between items-center gap-4 text-sm text-gray-400"> {{ page.footer_bottom }} </div> </div> </div> {% endif %} </footer>
</div>Template Product Card avec Tailwind
Section intitulée « Template Product Card avec Tailwind »{#/** * @file * Product card template with Tailwind CSS. */#}<article class="group card hover:-translate-y-1 transition-transform duration-200">
{# Badges #} <div class="absolute top-2 left-2 z-10 flex gap-1"> {% if is_on_sale %} <span class="badge badge-danger">-{{ discount_percent }}%</span> {% endif %} {% if node.isPromoted() %} <span class="badge badge-primary">Nouveau</span> {% endif %} </div>
{# Quick actions #} <div class="absolute top-2 right-2 z-10 flex flex-col gap-1 opacity-0 translate-x-2 group-hover:opacity-100 group-hover:translate-x-0 transition-all duration-200"> <button class="w-8 h-8 bg-white rounded-full shadow flex items-center justify-center text-gray-600 hover:text-primary hover:scale-110 transition-transform"> ♡ </button> <button class="w-8 h-8 bg-white rounded-full shadow flex items-center justify-center text-gray-600 hover:text-primary hover:scale-110 transition-transform"> 👁 </button> </div>
{# Image #} <a href="{{ url }}" class="block aspect-square overflow-hidden"> {% if content.field_images.0 %} <img src="{{ file_url(node.field_images.0.entity.fileuri) }}" alt="{{ node.field_images.0.alt }}" class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300" > {% else %} <div class="w-full h-full bg-gray-100 flex items-center justify-center text-gray-400"> No image </div> {% endif %} </a>
{# Info #} <div class="card-body flex flex-col">
{# Brand #} {% if node.field_brand.entity %} <span class="text-xs uppercase tracking-wider text-gray-500"> {{ node.field_brand.entity.label }} </span> {% endif %}
{# Title #} <h3 class="font-semibold text-gray-900 line-clamp-2 mt-1"> <a href="{{ url }}" class="hover:text-primary transition-colors"> {{ label }} </a> </h3>
{# Price #} <div class="mt-auto pt-2"> {% if is_on_sale and old_price %} <span class="text-sm text-gray-500 line-through mr-2">{{ old_price }}</span> <span class="text-lg font-bold text-danger">{{ formatted_price }}</span> {% else %} <span class="text-lg font-bold text-gray-900">{{ formatted_price }}</span> {% endif %} </div>
{# Colors #} {% if node.field_colors|length > 0 %} <div class="flex gap-1 mt-2"> {% for color in node.field_colors|slice(0, 5) %} <span class="w-4 h-4 rounded-full border border-gray-200" style="background-color: {{ color.entity.field_color_code.value }}" title="{{ color.entity.label }}" ></span> {% endfor %} {% if node.field_colors|length > 5 %} <span class="text-xs text-gray-500 self-center">+{{ node.field_colors|length - 5 }}</span> {% endif %} </div> {% endif %}
</div>
{# Add to cart button #} <div class="p-4 pt-0"> <button class="btn btn-primary w-full" data-product-id="{{ node.id }}" {% if not in_stock %}disabled{% endif %} > {% if in_stock %} Ajouter au panier {% else %} Indisponible {% endif %} </button> </div>
</article>⚙️ Workflow de développement
Section intitulée « ⚙️ Workflow de développement »Mode développement
Section intitulée « Mode développement »cd themes/custom/tailstorenpm run watchddev drush cr # Après modification des templatesMode production
Section intitulée « Mode production »# Build minifiénpm run build
# Vérifier la taillels -lh dist/assets/🔧 Intégration avec le formulaire exposé Views
Section intitulée « 🔧 Intégration avec le formulaire exposé Views »{# views-exposed-form.html.twig #}<form{{ attributes.addClass('flex flex-wrap gap-4 p-4 bg-gray-50 rounded-lg mb-8') }}>
{% for element in form %} {% if element['#type'] is defined and element['#type'] != 'hidden' %} <div class="flex-1 min-w-[150px]"> {% if element['#title'] %} <label class="form-label">{{ element['#title'] }}</label> {% endif %} {{ element|merge({'#attributes': {'class': ['form-input']}}) }} </div> {% endif %} {% endfor %}
<div class="flex gap-2 items-end"> <button type="submit" class="btn btn-primary"> Filtrer </button> {% if form['#info'] is defined and form['#info']['filter'] is defined %} <a href="{{ path('<current>') }}" class="btn btn-outline"> Réinitialiser </a> {% endif %} </div>
</form>📦 Optimisation production
Section intitulée « 📦 Optimisation production »Purge CSS
Section intitulée « Purge CSS »Tailwind v4 purge automatiquement les classes non utilisées. Assurez-vous que tous les templates sont listés dans content.
Vérifier les classes dynamiques
Section intitulée « Vérifier les classes dynamiques »Si vous générez des classes dynamiquement, utilisez la syntaxe complète :
{# ❌ Ne fonctionne pas - classe coupée #}<div class="bg-{{ color }}">
{# ✅ Fonctionne - classe complète dans le code #}{% set color_classes = { 'red': 'bg-red-500', 'blue': 'bg-blue-500', 'green': 'bg-green-500',} %}<div class="{{ color_classes[color] }}">Classes dynamiques avec Tailwind v4
Section intitulée « Classes dynamiques avec Tailwind v4 »Pour les classes générées dynamiquement, utilisez des noms de classes complets plutôt que des concaténations. Si vous avez besoin de classes vraiment dynamiques, considérez l’approche des variants safelist dans votre fichier CSS avec @theme.
✅ Checklist de validation
Section intitulée « ✅ Checklist de validation »Installation et configuration
Section intitulée « Installation et configuration »-
Vérifier l’installation des dépendances
Fenêtre de terminal cd themes/custom/tailstorels node_modules/@tailwindcss/vite node_modules/vite node_modules/tailwindcssAttendu : Les 3 dossiers existent
-
Vérifier vite.config.js
Fenêtre de terminal cat vite.config.js | grep "@tailwindcss/vite"Attendu :
import tailwindcss from '@tailwindcss/vite' -
Vérifier assets/style.css
Fenêtre de terminal cat assets/style.css | head -5Attendu :
@import "tailwindcss";présent -
Tester le build
Fenêtre de terminal npm run buildls -lh dist/assets/Attendu : Fichier
main-*.csscréé (< 20 KB) -
Vérifier la librairie Drupal
Fenêtre de terminal cat tailstore.libraries.yml | grep -A 5 "global:"Attendu : Référence à
dist/assets/main-*.css -
Tester dans le navigateur
Fenêtre de terminal drush cr# Ouvrir une page, F12 → Network → Filtrer "css"Attendu : Le fichier CSS Tailwind est chargé (200 OK)
-
Vérifier les classes appliquées
- Inspecter un élément (F12)
- Chercher une classe Tailwind (ex:
bg-blue-600) Attendu : Les styles sont appliqués
Tests fonctionnels
Section intitulée « Tests fonctionnels »- Les classes Tailwind sont appliquées (couleurs, espacements)
- Les composants personnalisés fonctionnent (
.btn,.card) - Le responsive fonctionne (tester mobile, tablette, desktop)
- Les variables
@themesont accessibles - Le mode watch fonctionne (
npm run dev) - Le build production est optimisé (< 15 KB après gzip)
- Pas de classes inutilisées dans le CSS final
- Les templates Twig utilisent les classes Tailwind
Performance
Section intitulée « Performance »- Taille CSS production < 15 KB
- Temps de build < 2 secondes
- Hot Module Replacement (HMR) fonctionne en dev
- Aucune erreur console liée aux styles
Script de validation complet
Section intitulée « Script de validation complet »#!/bin/bash
echo "=== Validation Tailwind CSS ==="
cd themes/custom/tailstore
# 1. Vérifier l'installationecho "\n📦 Dépendances :"npm list @tailwindcss/vite tailwindcss vite 2>&1 | grep -E "@tailwindcss|tailwindcss@|vite@"
# 2. Vérifier la configecho "\n⚙️ Configuration :"if [ -f "vite.config.js" ]; then echo "✅ vite.config.js existe"else echo "❌ vite.config.js manquant"fi
# 3. Vérifier le CSS sourceecho "\n🎨 Fichier CSS source :"if grep -q "@import.*tailwindcss" assets/style.css 2>/dev/null; then echo "✅ @import tailwindcss trouvé"else echo "❌ @import tailwindcss manquant"fi
# 4. Build et vérifier la tailleecho "\n🏗️ Build production :"npm run build > /dev/null 2>&1if [ -d "dist/assets" ]; then echo "✅ Build réussi" ls -lh dist/assets/*.css | awk '{print " Taille: " $5}'else echo "❌ Build échoué"fi
# 5. Vérifier la déclaration Drupalecho "\n📚 Librairie Drupal :"if grep -q "dist/assets" tailstore.libraries.yml 2>/dev/null; then echo "✅ Librairie déclarée"else echo "❌ Librairie non déclarée"fi
echo "\n✅ Validation terminée"Temps estimé total : 2-3 heures pour configuration + conversion des premiers templates
🚨 Erreurs courantes et solutions
Section intitulée « 🚨 Erreurs courantes et solutions »Erreur 1 : “Cannot find module ‘@tailwindcss/vite’”
Section intitulée « Erreur 1 : “Cannot find module ‘@tailwindcss/vite’” »Symptôme : Erreur au lancement de npm run dev
Cause : Dépendances non installées
Solution :
cd themes/custom/tailstorerm -rf node_modules package-lock.jsonnpm installErreur 2 : Classes Tailwind non appliquées
Section intitulée « Erreur 2 : Classes Tailwind non appliquées »Symptôme : Le CSS est chargé mais les classes ne fonctionnent pas
Cause : CSS non rebuilder après modification des templates
Solution :
# 1. Rebuilder le CSSnpm run build
# 2. Vider le cache Drupaldrush cr
# 3. Vider le cache navigateur (Ctrl+Shift+R)Erreur 3 : Fichier CSS trop volumineux (> 50 KB)
Section intitulée « Erreur 3 : Fichier CSS trop volumineux (> 50 KB) »Symptôme : Le fichier dist/assets/main-*.css fait plus de 50 KB
Cause : La purge ne fonctionne pas correctement
Solution :
// vite.config.js - Spécifier les fichiers à scannerexport default defineConfig({ plugins: [ tailwindcss({ content: [ './templates/**/*.twig', './templates/**/*.html.twig', ], }), ],})# Rebuildernpm run buildls -lh dist/assets/main-*.cssErreur 4 : “Failed to load config from vite.config.js”
Section intitulée « Erreur 4 : “Failed to load config from vite.config.js” »Symptôme : Erreur de syntaxe dans vite.config.js
Cause : Import incorrect ou syntaxe invalide
Solution :
// ❌ Mauvaisimport tailwindcss from '@tailwindcss/vite'export default { plugins: [tailwindcss()],}
// ✅ Bonimport { defineConfig } from 'vite'import tailwindcss from '@tailwindcss/vite'
export default defineConfig({ plugins: [tailwindcss()], build: { outDir: 'dist', },})Erreur 5 : Classes dynamiques purgées
Section intitulée « Erreur 5 : Classes dynamiques purgées »Symptôme : Classe générée dynamiquement ne fonctionne pas
Cause : Tailwind ne voit pas la classe complète dans le code
Solution :
{# ❌ Ne fonctionne pas - classe dynamique #}<div class="bg-{{ color }}-500">
{# ✅ Fonctionne - mapping explicite #}{% set color_map = { 'red': 'bg-red-500', 'blue': 'bg-blue-500', 'green': 'bg-green-500',} %}<div class="{{ color_map[color] }}">
{# Ou utiliser style inline pour les couleurs vraiment dynamiques #}<div style="background-color: {{ color }};">Erreur 6 : HMR (Hot reload) ne fonctionne pas
Section intitulée « Erreur 6 : HMR (Hot reload) ne fonctionne pas »Symptôme : Modifications CSS non visibles en temps réel
Cause : Vite dev server non lancé
Solution :
# Terminal 1 : Vite watchcd themes/custom/tailstorenpm run dev
# Terminal 2 : DDEV (Drupal)ddev start
# Les modifications CSS sont maintenant instantanéesErreur 7 : “@theme is not defined”
Section intitulée « Erreur 7 : “@theme is not defined” »Symptôme : Erreur de parsing CSS
Cause : Mauvaise syntaxe dans @theme
Solution :
/* ❌ Mauvais */@theme { color-primary: #0073e6; /* Manque -- */}
/* ✅ Bon */@theme { --color-primary: #0073e6; --color-secondary: #6c757d;}📚 Ressources complémentaires
Section intitulée « 📚 Ressources complémentaires »Documentation officielle
Section intitulée « Documentation officielle »- Tailwind CSS v4 - Documentation complète
- Vite Plugin - Intégration Vite
- Customization - Personnalisation @theme
- Responsive Design - Breakpoints
- Utility-First - Philosophie
Outils recommandés
Section intitulée « Outils recommandés »- Tailwind CSS IntelliSense (VS Code) : Autocomplétion des classes
- Headwind (VS Code) : Tri automatique des classes
- Prettier + plugin Tailwind : Formatage cohérent
- Tailwind Play : Playground en ligne pour tester
Ressources UI
Section intitulée « Ressources UI »- Tailwind UI - Composants officiels (payant)
- Flowbite - Composants open-source
- daisyUI - Composants Tailwind
- Headless UI - Composants accessibles sans styles
Exemples e-commerce
Section intitulée « Exemples e-commerce »- E-commerce Template - Template complet
- Product Cards - Exemples cartes produits
🔜 Prochaine étape
Section intitulée « 🔜 Prochaine étape »Tailwind est intégré ! Ajoutons maintenant Alpine.js pour les interactions.