Je ne déteste plus TailwindCSS

Posté le 15 janvier 2026 - Bonnes Pratiques - Par Grafikart - Proposer une correction

En tant que développeur, on a en général une opinion assez forte sur les outils et technologies que l'on utilise, mais ces avis tranchés ne survivent pas forcément bien le passage du temps. On peut passer d'un truc qu'on déteste et que l'on cherche à éviter à tout prix, à quelque chose qui devient partie intégrante de notre boîte à outil. Aujourd'hui, je vous propose de parler d'une de ces technologie, pour laquelle j'ai fait un 180 degré : TailwindCSS.

Rappel, TailwindCSS c'est quoi ?

Pour ceux qui ne connaîtraient pas, TailwindCSS est un outil qui permet de générer du CSS à partir des classes que l'on applique à nos éléments HTML. Plutôt que de créer un CSS séparé, on va venir ajouter une combinaison de classes utilitaires à nos éléments pour pouvoir les styliser.

Si je vous donne un exemple concret avec un petit bouton : j'ai envie qu'il ait une couleur de fond bleu

  • J'ajoute une classe bg-blue-500 pour avoir un fond bleu et text-white pour la couleur du texte.
  • Je rajoute rounded pour arrondir la forme
  • J'ajoute un peu de padding px-4 py-2 pour que le bouton respire.
<button class="bg-blue-500 rounded text-white px-4 py-2">Mon bouton</button>

Et automatiquement, Tailwind va venir générer le CSS qui correspond à mon élément.

Pourquoi j'ai détesté ?

Quand j'ai vu cette approche pour la première fois, j'ai vraiment détesté car, pour moi, ça allait à l'encontre de tout ce que j'avais appris. Au point même de devoir inviter RomainLanz pour en parler plus positivement dans ma vidéo de présentation de Tailwind.

Pour comprendre ce rejet, il faut revenir un petit peu à l'origine. Quand j'ai commencé à créer des pages web, le CSS n'existait pas. À l'époque, pour styliser nos pages, on utilisait des tableaux pour toute la partie structure, et on avait une balise <font> pour changer les styles de texte. Le problème, c'est que ce n'était absolument pas réutilisable, pas pratique à utiliser, et en plus de ça, on mélangeait le fond et la forme.

Quand le CSS est arrivé, ça a été vraiment un bouleversement, parce qu'on a pu séparer le fond et la forme. Côté HTML, on met des classes à nos éléments, et ensuite en CSS, on applique un style particulier. Une très bonne démonstration de ça, c'est le site CSS Zen Garden : un même HTML avec des feuilles de style différentes qui donnent des designs radicalement différents.

Aussi, quand j'ai vu Tailwind, j'ai vu un retour en arrière. Un passage de cette approche où on sépare fond et forme à un retour dans le passé avec du style directement dans nos éléments. Ça allait à l'encontre de tout ce que j'avais appris et connu.

Pourquoi j'ai changé d'avis ?

Le problème du nommage

À force de travailler avec le CSS, j'ai commencé à rencontrer LE problème que tous les développeurs CSS connaissent : Nommer les classes.

Ce que j'avais tendance à faire, c'est de nommer les choses sémantiquement. Par exemple, un titre d'article, je lui donnais la classe .post-title. La date, c'était .post-date. Le problème, c'est que si j'avais besoin d'avoir la date à gauche et le nom de l'auteur à droite, ça m'obligeait à trouver un nom pour l'élément qui englobe les deux .post-meta-wrapper.

Et très rapidement, on se retrouve avec des noms qui ont de moins en moins de sens, et à la fin du projet on a une bouillie de noms de classe. C'est impossible pour un nouveau développeur de connaître les classes qu'il peut utiliser (et même pour un développeur expérimenté, il y a une limite à ce que l'on peut mémoriser).

L'évolution vers les composants

La deuxième raison qui m'a poussé à changer d'avis, c'est la manière de décomposer les projets. Pendant longtemps on travaillait directement depuis la maquette et on intégrait les éléments au fur et à mesure qu'on les rencontrait en essayant d'identifier des motifs récurrents. Sauf que la manière de travailler des designers a pas mal changé.

Ce que font les designers aujourd'hui, c'est qu'ils travaillent avec des styles de texte, des palettes de couleurs, et surtout ils décomposent leur maquette en composants réutilisables. Cette approche leur permet d'être plus flexible vis à vis des demandes clients. Si un élément doit changer d'apparence, ils peuvent le modifier à un endroit, et cela affectera tout le design.

L'avantage c'est que nous, en tant que développeurs, on peut faire la même chose en créant des composants qui correspondent à ceux qui sont fait par le designer

// post-card.html
<card>
  <card-title>{{ product.name }}</card-title>
  <card-content>
    <div class="flex justify-between">
      <div><icon name="clock" />{{ post.date }}</div>
      <div>par {{ post.author }}</div>
    </div>
    <p>{{ post.excerpt }}</p>
    <app-button variant="primary" href="#">Voir l'article</app-button>
  </card-content>
</card>

Ensuite, c'est ce composant-là qui va être utilisé dans nos pages (et nos autres composants). Avec ce découpage une nouvelle séparation est apparue : là où avant on avait notre HTML, nos classes et notre CSS, aujourd'hui on a notre HTML, nos composants et le style et on se retrouve avec un nouveau niveau de d'abstraction.

Et c'est là que se situe le problème. Si on décide de continuer à faire des classes CSS classiques, on se retrouve avec une étape intermédiaire un peu redondante.

<button
  class={cls(
    variant === "primary" && "btn-primary",
    variant === "secondary" && "btn-secondary",
    variant === "destructive" && "btn-danger",
    size === "sm" && "btn-sm",
    size === "md" && "btn-md",
  )}
>
  {content}
</button>

On doit appliquer les bonnes classes en fonction des différents cas possible pour ensuite créer le CSS correspondant. Lors de la modification du composant, on se retrouve alors à devoir faire des modifications à 2 endroits différent.

  • Au niveau de l'HTML on ajoute / modifie les classes.
  • Au niveau du CSS il faut trouver le sélecteur correspondant et le mettre à jour.

Et c'est en travaillant sur des projets clients que j'ai réalisé que le fait d'avoir directement les styles au niveau du composant, c'était plus pratique, ça évite de nombreux aller-retours.

En étant forcé de passer au dessus de mes a priori et mes principes, j'ai découvert que l'utilisation de TailwindCSS n'était pas forcément contre-productive et que cela pouvait au contraire apporter des solutions au problème de nommage et aider le travail lorsque l'on découpe le projet en composant.

L'écosystème de librairies

Le dernier point qui a facilité mon changement de position c'est l'apparition de librairies de composant respectant la philosophie de TailwindCSS. En effet, travailler avec TailwindCSS sur un projet pour lequel le client n'a pas de design (pour un dashboard par exemple) était très difficile. TailwindCSS offre un ensemble de classe utilitaires mais c'est à vous de créer une structure cohérente à partir de ça (tout est à faire). Des outils comme bootstrap avaient une philosophie différente et offraient des classes permettant d'avoir facilement une interface agréable et cohérente avec des classes liées au fonctionnement des composants (.table, .btn, .card...).

Mais récemment de nouvelles librairies comme shadcn ont débarquées et proposent un catalogue de composant cohérent avec un découpage des couleurs intelligent. Ce qui permet d'avoir une bonne base de travail tout en ayant la possibilité de personnaliser le moindre élément si on en a besoin. Cela permet de rendre l'utilisation de TailwindCSS viable, même si on n'a pas de design comme base de travail.

Même si je ne suis pas un grand utilisateur, l'IA joue aussi un rôle important dans l'adoption de TailwindCSS car ça permet aux modèles de générer un composant (ou une page) en proposant simplement de l'HTML avec des classes utilitaires. L'utilisateur n'a plus qu'à copier le code dans son site (en modifiant si nécessaire les classes pour adapter) et le composant est fonctionnel.

La réalisation

Ce que j'ai réalisé, c'est que le problème que j'avais avec le mélange fond/forme n'est pas nécessairement vrai si on utilise TailwindCSS. La séparation fond / forme on l'a fait maintenant à un autre niveau : au niveau des composants. Comme si on avait accès à un éventail quasiment infini de nouvelles balises HTML pour représenter notre contenu. Maintenant, si je veux changer le style d'un élément de mon site (par exemple les boutons), je change le style de mon composant, et cela va effecter l'ensemble de mon application.

Tout n'est pas rose !

Même si aujourd'hui j'utilise quasi systématiquement TailwindCSS il subsiste quelques points de frictions.

Les couleur

Premier point que je trouve irritant c'est l'utilisation des couleurs de bases dans l'HTML : par exemple bg-blue-500. Pourquoi ?

En général, quand on travaille avec un design, on va avoir une couleur qui va être réutilisée à plusieurs endroits. Si à chaque fois que vous voyez cette couleur, vous mettez bg-blue-200, ça veut dire que si demain le designer décide de la changer, vous devrez faire beaucoup de changement dans votre code.

Ce que je conseille, c'est de prendre les variables qui sont utilisées dans la maquette par le designer et d'utiliser ces couleurs comme bases dans TailwindCSS :

@import "tailwindcss";

@theme {
  --color-*: initial;
  --color-primary: #3b82f6;
  --color-primary-foreground: #ffffff;
  --color-secondary: #6b7280;
  --color-secondary-foreground: #ffffff;
  --color-background: #ffffff;
  --color-foreground: #0a0a0a;
  --color-card: #ffffff;
  --color-card-foreground: #0a0a0a;
  --color-muted: #f5f5f5;
  --color-muted-foreground: #737373;
  --color-border: #e5e5e5;
  --color-input: #e5e5e5;
  --color-ring: #3b82f6;
}

Ensuite quand on a besoin de mettre une couleur, plutôt que d'utiliser bg-blue-200, on utilise bg-primary. D'ailleurs, si vous êtes en panne d'inspiration pour classer vos couleurs vous pouvez vous inspirer des variables utilisées par shadcn qui propose, selon moi, une très bonne base.

Ne pas oublier le CSS classique

Second point qui peut parfois ruiner un composant, c'est d'oublier qu'on peut faire du CSS à côté de Tailwind et finir avec un trop grand nombre de classe ou des classes trop complexe.

<table class="[&_tr:nth-child(2n)_td]:bg-muted w-full"></table>

Exemple concret : imaginons qu'on ait un tableau et qu'on veuille qu'une ligne sur deux soit un petit peu plus grisée. On pourrait :

  • Manuellement trouver un td sur 2 pendant la boucle et lui rajouter une classe (pas pratique)
  • Utiliser un sélecteur complexe au niveau du td (pas terrible)
  • Le faire au niveau du table avec un sélecteur en profondeur (problème de spécificité)

La solution la plus simple, c'est de revenir à du CSS classique :

@layer components {
  .table-striped tbody tr:nth-child(even) td {
    background-color: var(--color-muted);
  }
}

/* si on souhaite plutôt avoir un utilitaire tailwind */
@utility table-striped {
  @layer components {
    & tbody tr:nth-child(even) td {
      @apply bg-muted;
    }
  }
}

En utilisant le layer components, les classes utilitaires restent prioritaires, donc je peux toujours venir changer la couleur de fond d'un td avec une classe utilitaire si je le souhaite.

<table class="[&_tr:nth-child(2n)_td]:bg-muted w-full">
  <!-- ... -->
</table>

Je n'aime pas les développeurs qui sont complètement enfermés avec Tailwind et qui cherchent comment faire un truc simple en CSS de manière complexe avec TailwindCSS.

Attention à la lisibilité

Il y a aussi tout l'aspect gestion des états. Si vous voulez gérer l'état actif suivant que l'élément soit focus ou qu'il ait un attribut data-*, ça peut rapidement donner des sélecteurs compliqués.

// Exemple de l'input shadcn
<input
  type={type}
  data-slot="input"
  className={cn(
    "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
    "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
    "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
    className,
  )}
  {...props}
/>

Sommaire

  • 00:00 Introduction
  • 00:44 C'est quoi Tailwind ?
  • 01:19 Pourquoi j'ai détesté ?
  • 02:43 Pourquoi j'ai changé d'avis ?
  • 02:54 Difficulté à nommer les choses en CSS
  • 03:33 Découpage en composant
  • 06:09 Des UI Kits quand on a pas de design
  • 08:05 Les points que je n'aime toujours pas
  • 11:20 Conclusion
    Là je n'ai pas forçément de solution à proposer si ce n'est de regrouper les classes en fonction de leur catégorie dans différente chaine de caractères (comme c'est fait ici pour aria-invalid)

Conclusion

TailwindCSS est un cas un peu particulier car, contrairement à d'autres technologies pour lesquelles j'ai changé d'avis suite à des changements, c'est un changement dans ma manière de travailler qui a fait changer ma perspective sur l'outil. Dans ce nouveau monde où on découpe tout en composants TailwindCSS est une très bonne approche pour appliquer du style à nos éléments.