Plan de migration vers Tailwind CSS v4 🚀 : la méthode (presque) sans douleur
Ça y est, le grand jour est arrivé ! Vous avez enfin décidé de vous attaquer à cette fameuse dette technique qui vous fait faire des cauchemars la nuit. 😅
Beaucoup de nos projets (et sans doute les vôtres) reposent encore sur d’anciens frameworks CSS basés sur Sass, comme Bootstrap v4 ou d’autres solutions maison (coucou Atomic Builder 👋). Et soyons honnêtes : maintenir et mettre à jour ces projets relève souvent du parcours du combattant. On fait face à un manque cruel de flexibilité, la dette technique s’accumule, et les dépendances commencent à poser plus de soucis qu’elles n’en résolvent.
Sans oublier que Sass, bien qu’ayant été notre fidèle compagnon pendant de très (très) nombreuses années, devient de moins en moins indispensable avec l’évolution fulgurante du CSS natif. Nous avons d’ailleurs eu l’occasion d’en parler dans notre article expliquant pourquoi passer à PostCSS.
Le choix s’est donc naturellement porté vers Tailwind CSS v4. C’est une solution qui a fait ses preuves chez JoliCode (J’ai testé Tailwind CSS) et, cerise sur le gâteau, cette nouvelle version ne nécessite aucune dépendance liée à Sass.
L’année dernière, nous avons amorcé des phases de migration pour plusieurs de nos projets clients. Aujourd’hui, j’ai envie de partager avec vous quelques retours d’expérience et une méthodologie (testée et approuvée ✅) pour que ces migrations se passent le mieux possible !
Section intitulée phase-1-migration-des-feuilles-de-styles-cssPhase 1 : Migration des feuilles de styles CSS 🎨
L’objectif de cette première phase est simple : utiliser du CSS natif et dire officiellement au revoir à notre bon vieux Sass. 👋
La toute première étape consiste à migrer la configuration globale de votre projet (généralement définie dans des fichiers tels que variables.scss ou global.scss) vers un système basé sur des custom properties (variables CSS), en ciblant la racine du document HTML via la pseudo-classe :root.
Avant (Sass) :
$color-primary: #f7d325;
$color-secondary: #ff2951;
$font-family-sans-serif: "Source Sans Pro", sans-serif;
Après (CSS natif) :
:root {
--color-primary: #f7d325;
--color-secondary: #ff2951;
--font-sans: "Source Sans Pro", sans-serif;
}
Info
Utilisez dès maintenant la nomenclature de Tailwind CSS v4 pour nommer vos variables CSS. Ça vous fera gagner un temps précieux pour la suite. 👌
Section intitulée cas-n-1-les-variables-et-fonctionsCas n°1 : Les variables et fonctions
Une fois votre thème mis en place, votre première mission sera de remplacer tous vos $ par des var(--). C’est aussi l’occasion de traduire vos fonctions Sass en CSS moderne. 👀
Avant (Sass) :
.button {
width: math.div(100%, 3);
background-color: $color-primary;
&:hover,
&:focus-visible,
&:active {
background-color: darken($color-primary, 15%);
}
}
Après (CSS natif) :
.button {
/* Le CSS natif s'occupe des calculs dynamiquement ! */
width: calc(100% / 3);
background-color: var(--color-primary);
&:hover,
&:focus-visible,
&:active {
/* Magie ! Les couleurs relatives en CSS natif */
background-color: hsl(from var(--color-primary) h s calc(l - 15));
}
}
Aujourd’hui, le CSS natif est devenu suffisamment mature pour remplacer la quasi-totalité des fonctions Sass. C’est particulièrement vrai pour les manipulations de couleurs, qui s’écrivent désormais très simplement grâce aux couleurs relatives en CSS. 🎨
Section intitulée cas-n-2-le-piege-du-selecteur-d-imbrication-code-amp-code-et-du-bemCas n°2 : Le piège du sélecteur d’imbrication & et du BEM
La bonne nouvelle, c’est que le CSS natif gère désormais très bien le nesting (l’imbrication). L’esperluette (&) pour cibler des états comme &:hover ou &:focus fonctionne parfaitement sans Sass.
La mauvaise nouvelle ? Si vous utilisiez le & pour concaténer des classes, typiquement avec la méthodologie BEM (&__element ou &--modifier), le CSS natif ne comprendra pas cette syntaxe. Il va falloir « désimbriquer » ces éléments.
Avant (Sass) :
.link {
color: $color-primary;
&:hover,
&:focus-visible,
&:active {
color: $color-secondary;
}
/* Sass va compiler ce code en ".link--secondary" */
&--secondary {
color: $color-secondary;
}
}
Après (CSS natif) :
.link {
color: var(--color-primary);
/* Ce code fonctionne toujours en CSS natif ! */
&:hover,
&:focus-visible,
&:active {
color: var(--color-secondary);
}
}
/* Il faut sortir la classe enfant de l'imbrication */
.link--secondary {
color: var(--color-secondary);
}
Section intitulée cas-n-3-les-media-queriesCas n°3 : Les media queries
Pour les media queries générées via des mixins ou des fonctions Sass (du type @include media-breakpoint-up(md)), passez simplement les valeurs en dur pour le moment (@media (min-width: 768px)). Nous nous en occuperons plus tard, lors de l’intégration de Tailwind.
Section intitulée cas-n-4-les-mixins-boucles-et-regles-complexesCas n°4 : Les mixins, boucles et règles complexes
Pour les cas très spécifiques (boucles @for, mixins complexes), la solution la plus pragmatique est de récupérer la version déjà compilée et de la coller dans votre fichier CSS.
Avant (Sass) :
@for $i from 1 through 3 {
.button--size-#{$i} {
padding: #{$i}rem;
}
}
Après (CSS natif) :
.button--size-1 { padding: 1rem; }
.button--size-2 { padding: 2rem; }
.button--size-3 { padding: 3rem; }
À la fin de cette phase, le code CSS de votre projet n’utilise techniquement plus aucune fonctionnalité propre à Sass. 👏
Section intitulée phase-2-migration-provisoire-des-classes-utilitairesPhase 2 : Migration (provisoire) des classes utilitaires 🧰
C’est l’étape de la « prise de masse » avant la sèche. 💪
L’idée est de récupérer le CSS compilé de toutes les classes utilitaires de votre ancien framework et de les stocker dans un nouveau dossier, par exemple utilities/. Pour garder l’esprit clair, séparez-les par thématique : utilities/typography.css, utilities/spacing.css, etc.
Exemple d’un fichier utilities/spacing.css (récupéré depuis Bootstrap v4) :
.mt-1 { margin-top: 0.25rem !important; }
.mt-2 { margin-top: 0.5rem !important; }
.mb-3 { margin-bottom: 1rem !important; }
/* ... */
Votre CSS global va grossir temporairement. C’est normal, ne paniquez pas ! Ces fichiers agiront comme un filet de sécurité visuel en attendant que Tailwind prenne le relais. 😮💨
Section intitulée phase-3-on-debranche-l-ancien-framework-cssPhase 3 : On débranche l’ancien framework CSS 💔
Si les phases 1 et 2 ont été faites minutieusement, vous pouvez supprimer l’ancien framework CSS de vos dépendances. Visuellement, rien ne devrait bouger. 🤞
Avertissement
Attention aux comportements JS ! Si votre ancien framework (comme Bootstrap) gérait des modales, des info-bulles ou des menus déroulants, la suppression de son CSS (et de ses scripts associés) risque de casser les animations ou le positionnement. Prévoyez un peu de temps de développement pour vérifier les interactions JavaScript et réécrire ces quelques composants. Profitez-en pour utiliser du JS vanilla ou du CSS moderne (l’API Popover native est parfaite pour ça, par exemple !).
Section intitulée phase-4-l-adieu-a-sassPhase 4 : L’adieu à Sass 🫡
C’est l’heure de désinstaller une fois pour toutes sass (ou node-sass). N’oubliez pas de renommer tous vos fichiers .scss en .css.
Lors de cette étape, il est très probable que le compilateur échoue lors de son premier lancement. Si la console se remplit d’erreurs, pas de panique, c’est un grand classique ! Cela s’explique généralement par l’une de ces trois raisons :
- L’import fantôme : Votre bundler (comme Webpack) ou votre point d’entrée JavaScript (le fameux
app.js) pointe toujours vers un fichier portant l’extension.scss. Pensez à bien mettre à jour vos chemins d’importation. - Le reliquat de Sass : Il reste quelques
$,@use, etc. qui traînent dans vos fichiers CSS. Pensez à les supprimer. - Les commentaires : Les commentaires d’une seule ligne écrits avec
//étaient valides avec Sass, mais pas avec votre CSS natif.
L’erreur classique à corriger :
// Ce commentaire était valide en Sass, mais fera planter votre CSS natif !
.element { color: var(--color-primary); }
/* Remplacez-le par la syntaxe standard ! */
.element { color: var(--color-primary); }
Une fois ces petits ajustements faits, respirez un grand coup. Le plus dur techniquement est derrière vous. 🎉
Section intitulée phase-5-l-entree-en-scene-de-tailwind-css-v4Phase 5 : L’entrée en scène de Tailwind CSS v4 🤩
Il est enfin temps d’installer Tailwind CSS v4 et PostCSS (indispensable dans nos projets Symfony avec Webpack Encore). Je vous conseille de suivre la documentation officielle pour le setup initial.
Section intitulée le-piege-de-l-import-global-et-du-preflightLe piège de l’import global (et du Preflight)
La documentation officielle vous recommandera d’importer Tailwind de cette façon :
@import "tailwindcss";
En utilisant cet import global, Tailwind embarque par défaut plusieurs layers CSS (theme, base, components et utilities), injectant par la même occasion son propre reset CSS (Preflight). Si votre projet possédait déjà un fichier de reset (comme normalize.css), vous risquez d’avoir des conflits.
Info
Désactivez Preflight dans un premier temps, ainsi que l’ensemble des layers CSS importés pour limiter les régressions et les problématiques liées à la cascade CSS.
Pour cela, privilégiez des imports ciblés (sans les layers CSS) :
@import "tailwindcss/theme.css";
@import "tailwindcss/utilities.css";
Section intitulée la-magie-de-la-directive-code-theme-codeLa magie de la directive @theme
La révolution de la v4, c’est qu’elle est CSS-first. Fini l’énorme fichier tailwind.config.js à la racine de votre projet ! Le thème que vous aviez anticipé lors de la phase 1 s’intègre désormais presque nativement grâce à la nouvelle directive @theme.
Avant :
:root {
--color-primary: #f7d325;
--color-secondary: #ff2951;
--font-sans: "Source Sans Pro", sans-serif;
}
Après :
@theme static {
--color-primary: #f7d325;
--color-secondary: #ff2951;
--font-sans: "Source Sans Pro", sans-serif;
}
Info
Gardez vos variables CSS accessibles partout ! Pensez à générer vos variables CSS avec le comportement static, comme indiqué dans la documentation Tailwind. Ainsi, elles ne seront pas limitées aux classes utilitaires de Tailwind, mais resteront utilisables partout dans vos propres composants CSS personnalisés.
C’est aussi le moment parfait pour utiliser la directive @variant afin d’uniformiser ces fameuses media queries que nous avions mises en dur plus tôt !
Avant :
@media (min-width: 768px) {
.card {
color: var(--color-primary);
}
}
Après :
@variant md {
.card {
color: var(--color-primary);
}
}
Ce petit changement vous garantit que tout votre CSS sur-mesure restera parfaitement synchronisé avec le comportement natif des classes utilitaires de Tailwind.
Section intitulée phase-6-le-nettoyage-de-printempsPhase 6 : Le nettoyage de printemps 🧹
Je préfère être honnête : cette étape demande de la patience. 🙃
L’objectif est de supprimer le dossier utilities/ créé à la phase 2. Pour ce faire, vous allez devoir remplacer dans tous vos templates HTML/Twig les anciennes classes par les nouvelles générées par Tailwind.
Vos meilleurs alliés ici seront l’outil de recherche de votre IDE, les expressions régulières (Regex), et bien sûr votre LLM (Large Language Model) favori (ChatGPT, Claude, GitHub Copilot, etc.) pour automatiser un maximum le processus.
Avant (Bootstrap v4) :
<div class="d-flex flex-column flex-md-row">...</div>
Après (Tailwind CSS v4) :
<div class="flex flex-col md:flex-row">...</div>
Section intitulée le-boss-final-les-grillesLe boss final : Les grilles 😎
Il n’y a pas de solution magique ici. Les vieux frameworks utilisaient généralement Flexbox pour simuler un comportement de grille. Tailwind, lui, utilise le module natif CSS Grid Layout (modèle de disposition en grille).
Il va falloir repasser sur vos grilles manuellement. 😬
L’ancienne approche (Flexbox) :
<div class="row">
<div class="col-12 col-md-6">...</div>
<div class="col-12 col-md-6">...</div>
</div>
La nouvelle approche (Grid) :
<div class="grid grid-cols-1 md:grid-cols-2">
<div>...</div>
<div>...</div>
</div>
C’est fastidieux, mais le DOM en ressort considérablement allégé et plus lisible. 🥳
Section intitulée conclusion-tout-ca-pour-caConclusion : Tout ça pour ça ? 🤔
Oui, mille fois oui. Migrer vers Tailwind CSS v4 n’est pas un parcours de santé pour un projet existant. Mais les avantages sont colossaux.
Vous dites adieu à l’usine à gaz qu’était devenu Sass au fil des années. Le poids final de votre CSS sera drastiquement réduit puisque Tailwind ne compile que ce que vous utilisez réellement. Sans parler des performances pures : avec son nouveau moteur écrit en Rust, les temps de compilation sont si rapides qu’on les remarque à peine.
Enfin, la Developer Experience (DX) de vos équipes s’en trouvera transformée. C’est un investissement en temps conséquent, mais c’est un cadeau inestimable que vous faites au futur de votre projet. 🎁
Section intitulée et-concretement-comment-on-vend-ca-a-sa-directionEt concrètement, comment on vend ça à sa direction ?
C’est souvent la question qui fâche. On sait que le chantier est nécessaire, mais il est difficile d’estimer le temps que cela va prendre sans y laisser des plumes, d’où l’effet tunnel qui effraie les équipes produit.
Pour débloquer la situation, nous avons l’habitude chez JoliCode de commencer par un audit flash (1 à 2 jours). L’objectif est de poser les choses à plat pour vous fournir un plan de bataille concret à défendre en interne :
- Un état des lieux clair de la dette front actuelle (dépendances, risques, obsolescence).
- Une estimation chiffrée de la migration, découpée par phase.
- Un plan de déploiement progressif qui ne bloque pas votre roadmap produit.
- Les bénéfices attendus (gains de performance, vélocité des équipes, etc.).
Si vous avez besoin d’aide pour cadrer votre propre migration, n’hésitez pas à nous faire signe !
J’espère que ce retour d’expérience vous sera utile et vous donnera le courage de sauter le pas. N’hésitez pas à partager vos propres astuces ou vos galères de migration dans les commentaires ! 🙂
Commentaires et discussions
Nos formations sur ce sujet
Notre expertise est aussi disponible sous forme de formations professionnelles !
CSS avancé
Mettre en forme des pages web en offrant des possibilités de personnalisation poussées
Ces clients ont profité de notre expertise
Refonte complète de la plateforme d’annonces immobilières de Cushman & Wakefield France. Connecté aux outils historiques, cette nouvelle vitrine permet une bien meilleure visibilité SEO et permet la mise en avant d’actifs qui ne pouvaient pas l’être auparavant.
Dans le cadre du renouveau de sa stratégie digitale, Orpi France a fait appel à JoliCode afin de diriger la refonte du site Web orpi.com et l’intégration de nombreux nouveaux services. Pour effectuer cette migration, nous nous sommes appuyés sur une architecture en microservices à l’aide de PHP, Symfony, RabbitMQ, Elasticsearch et Docker.
Nous avons entrepris une refonte complète du site, initialement développé sur Drupal, dans le but de le consolider et de jeter les bases d’un avenir solide en adoptant Symfony. La plateforme est hautement sophistiquée et propose une pléthore de fonctionnalités, telles que la gestion des abonnements avec Stripe et Paypal, une API pour l’application…