Développement 7 min de lecture

Tailwind : la meilleure et la pire chose arrivée au développement front-end

James Barnes Ingénieur design principal

J’ai un aveu à faire : je pense que Tailwind est à la fois la pire et la meilleure chose qui soit arrivée au développement front-end, et je l’utilise quand même.

Chaque fois que j’ouvre les outils de développement sur un projet Tailwind, une petite partie de moi meurt. Chaque fois que je livre un composant en deux fois moins de temps qu’avant, cette partie revient à la vie. Ce billet, c’est ma tentative de faire la paix avec ces deux sentiments.

Le pire : bonne chance pour trouver quoi que ce soit

Voici une ligne à peine inventée, tirée d’un code en production :

<div class="flex items-center justify-between gap-4 rounded-lg border border-gray-200 bg-white px-4 py-3 shadow-sm hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-900 md:px-6">

C’est quoi, ça ? Une carte ? Une ligne de liste ? Un élément de navigation ? L’en-tête d’une fenêtre modale ? Vous n’en avez aucune idée, et personne d’autre qui ouvre l’inspecteur non plus. Le bon vieux CSS vous donnait .product-card ou .checkout-summary. Le nom de classe vous disait ce que la chose était. Tailwind vous dit seulement de quoi elle a l’air.

Ça semble être une petite perte, jusqu’à ce qu’on doive vivre avec :

  • Les outils de développement deviennent un jeu de devinettes. Vous cliquez dans un mur de div imbriqués, tous vêtus des douze mêmes utilitaires, en essayant de faire correspondre ce que vous voyez à l’écran avec le code dans votre éditeur.
  • Chercher dans le code ne sert à rien. Un grep sur flex items-center renvoie la moitié du projet. Il n’y a aucun identifiant unique à rechercher.
  • Les tests et l’automatisation se compliquent. Les sélecteurs des tests de bout en bout, les balises d’analytique et les extensions de navigateur s’accrochaient à des noms de classes significatifs. Maintenant, vous semez des data-testid partout pour ramener les identifiants que vous avez perdus.
  • Le balisage est tout simplement laid. Les longues chaînes de classes débordent sur plusieurs lignes, les diffs deviennent bruyants, et réviser une PR revient à lire du CSS à l’envers, une abréviation à la fois.

La réponse de Tailwind : « le composant est le nom ». Dans votre éditeur, c’est en grande partie vrai : vous voyez <ProductCard>. Mais le navigateur ne voit jamais les noms de vos composants. Il voit la soupe.

Le meilleur : c’est tellement rapide

Et pourtant. Impossible de le nier : Tailwind rend la mise en forme plus rapide et plus simple à bien des égards.

  • Fini de nommer les choses. Nommer les choses est l’un des deux problèmes difficiles de l’informatique, et Tailwind l’efface de votre journée. Plus de débats entre .card__header--active et .card-header.is-active.
  • Fini de changer de contexte. Vous stylez l’élément là où il vit. Plus d’allers-retours entre un fichier de composant et une feuille de style, plus de chasse à la règle qui l’emporte.
  • Fini le CSS mort. Supprimez un composant et ses styles partent avec lui. Personne n’a peur de toucher à une feuille de style globale, parce que personne n’en a.
  • Des contraintes intégrées. Les espacements, les couleurs et les tailles de texte viennent d’une échelle. p-4 est toujours p-4. Une équipe de dix obtient une interface bien plus cohérente que dix personnes qui écrivent des valeurs en pixels à main levée.
  • Les styles responsifs et d’état deviennent triviaux. Ce qui exigeait un bloc de media query ou un sélecteur à part n’est plus qu’à un préfixe : md:, hover:, dark:, focus-visible:.
  • La guerre de la spécificité est terminée. Tout est à une seule classe de profondeur. Fini la course aux !important.

Résultat : on arrête de penser à l’architecture CSS et on construit, tout simplement. Pour la plupart des projets produits, c’est exactement le bon compromis.

Pourquoi il a gagné : React n’a jamais eu de vraie réponse pour le style

Honnêtement, une bonne partie du succès de Tailwind, c’est la faute de React. React nous a appris à construire des composants, puis a haussé les épaules quand est venu le temps de les styler. Chaque option était un compromis :

  • Les objets style={{}} en ligne : du CSS en camelCase dans du JavaScript, sans états de survol, sans media queries, sans pseudo-éléments.
  • Les fichiers CSS globaux : complètement déconnectés des composants qui les utilisent, et à un mauvais sélecteur de briser quelque chose sur une autre page.
  • Les CSS Modules : une meilleure isolation, mais on revient à tout nommer et à jongler avec un deuxième fichier par composant.
  • Le CSS-in-JS (styled-components, Emotion) : une belle ergonomie, mais un coût à l’exécution, des bundles plus lourds et un mariage maladroit avec les composants serveur.

Tailwind s’est glissé parfaitement dans ce vide. Il est colocalisé comme les styles en ligne, mais il gère chaque état et chaque point de rupture. Il est isolé par défaut, puisqu’il n’y a rien de global avec quoi entrer en collision. Et il se compile en une petite feuille de style statique, ce qui le rend compatible avec le rendu côté serveur.

Dans un monde où le composant est déjà l’unité de réutilisation, les classes utilitaires cessent d’avoir l’air folles. Elles ressemblent au modèle de style que React aurait dû livrer d’origine.

Ce qui est un peu ironique, parce que Svelte et Vue ont déjà réglé ce problème. Ajoutez un bloc <style> dans un fichier .svelte, ou un bloc <style scoped> dans un composant monofichier Vue, et votre CSS est automatiquement isolé à ce composant. Vous obtenez du vrai CSS, de vrais sélecteurs, des noms de classes significatifs et la colocalisation, sans soupe d’utilitaires. Une bonne partie de ce qui fait passer Tailwind pour une révélation dans React, c’est simplement du rattrapage par rapport à ce que ces frameworks offraient déjà depuis des années.

Qu’on le veuille ou non, c’est devenu la norme

Même si vous préférez écrire du CSS pur, se passer de Tailwind devient de plus en plus difficile. Une bonne partie de l’écosystème d’interfaces le tient maintenant pour acquis :

  • shadcn/ui vous remet des composants sous forme de code source stylé entièrement avec des classes Tailwind.
  • Headless UI, Flowbite, daisyUI et bien d’autres sont conçus pour lui ou par-dessus lui.
  • Les gabarits, les trousses de démarrage et les exemples de documentation livrent de plus en plus du balisage Tailwind par défaut.
  • Les outils de codage par IA ont tendance à cracher des classes Tailwind, parce que c’est à ça que ressemble la majorité du code dont ils ont appris.

Le choix n’est donc plus vraiment « Tailwind ou pas ». Si vous voulez copier un composant, utiliser une trousse populaire ou lire la plupart des exemples modernes, vous allez lire et écrire des classes utilitaires. Tailwind a gagné en partie sur le mérite et en partie sur l’élan, et à ce stade, cet élan s’entretient tout seul.

Vivre avec : la vitesse sans le fouillis

Pas besoin de choisir un camp. Quelques habitudes règlent la majeure partie du problème :

  1. Ajoutez un repère sémantique par élément significatif. Une simple classe comme product-card ou un attribut data-component="ProductCard" à côté des utilitaires ne coûte rien et rend les outils de développement lisibles à nouveau. Tailwind se moque bien qu’une classe sans rapport traîne là.
  2. Extrayez les composants tôt. Si la même chaîne de classes apparaît trois fois, c’est un composant, pas un copier-coller. C’est là que l’argument « le composant est le nom » de Tailwind tient vraiment la route.
  3. Utilisez @apply avec parcimonie, mais utilisez-le. Pour les éléments vraiment globaux (typographie de base, réinitialisation des formulaires, contenu rédactionnel que vous ne contrôlez pas), une petite feuille de style reste le bon outil.
  4. Laissez un formateur trier vos classes. Le plugin Prettier officiel ordonne les utilitaires de façon cohérente, ce qui rend les longues chaînes lisibles d’un coup d’œil et les diffs beaucoup plus calmes.
  5. Regroupez les variantes avec des utilitaires. Des bibliothèques comme clsx, cva ou tailwind-merge vous permettent de nommer vos variantes (size: "sm", intent: "danger") au lieu d’aligner une soupe de classes conditionnelles.
  6. Installez l’extension de l’éditeur. L’aperçu au survol et l’autocomplétion retraduisent les abréviations cryptiques en vrai CSS quand vous avez besoin de comprendre ce qui se passe.

Le verdict

Tailwind échange la lisibilité dans le navigateur contre la vitesse dans l’éditeur. Pour la plupart des équipes qui travaillent en React, c’est une bonne affaire, et c’est pourquoi on le retrouve partout.

Mais ça reste un échange. Le balisage est un fouillis, l’inspecteur est un labyrinthe, et le sens sémantique que portaient autrefois les noms de classes CSS a disparu, à moins de le remettre vous-même. Alors utilisez Tailwind, profitez de livrer plus vite, puis rendez service à votre futur vous : laissez quelques vrais noms derrière vous pour la personne qui devra déboguer tout ça à 2 h du matin.

Continuer la lecture
enfr