Tailwind CSS : Mon avis

Rédigé par Etienne Publié le 19/10/2021

Tailwind CSS est source de discorde chez Soumettre.fr : 16 heures passées au téléphone, 2 engueulades, et 8 tacles à la gorge. Suite à une longue discussion sur Twitter, j'ai décidé d'en faire un article dédié. Attention, on va parler d'intégration, de HTML et de CSS.

Tailwind CSS : Mon avis

Sommaire

Lors de cet échange, j'ai beaucoup craché sur Tailwind CSS mais je vais remettre les choses plus proprement ici. Tailwind CSS est un framework CSS (comme son nom l'indique) qui est censé permettre aux développeurs de développer rapidement des interfaces jolies et fonctionnelles. L'avis qui suit est tout à fait personnel et j'ai vraiment l'impression de passer à côté de quelque chose avec ce framework car peu importe le sens dans lequel je le tourne, je n'arrive pas à voir son intérêt.

Pour remettre un peu de contexte, on utilise Tailwind CSS sur Soumettre.fr, qu'on essaye de supprimer petit à petit. Aujourd'hui, il y a 471 fichiers de template (dossier des vues). Oui, j'ai refait le calcul deux fois car j'ai bugué en voyant le chiffre.

Dans ces 471 fichiers, il y a aussi des templates de mail, de pdf... Les fichiers qui peuvent utiliser Tailwind CSS sont :

  • Les layouts,
  • les vues d'une page,
  • les composants Laravel.

On a beaucoup de composants réutilisables entre les pages, on a aussi des bouts de templates qui ne sont appelés que dans certains cas comme le template de l'affichage d'une tâche netlinking terminée par exemple.

Il y a des modifications toutes les semaines sur ces fichiers par trois personnes différentes. Il faut donc essayer de s'imaginer Tailwind CSS à ce niveau d'utilisation. On ne parle pas ici de 2 templates de page ou même d'un blog WordPress qui va avoir une dizaine de fichiers de template.

La cible du framework

D'après moi Tailwind répond à un besoin et une cible précise : les dev backend. Ces énergumènes n'en ont rien à faire du CSS, ça les saoule et ils ne veulent pas perdre de temps. En plus, c'est dorénavant le framework CSS par défaut de Laravel ce qui a, je pense, grandement aidé à le populariser avec la TALL stack (Tailwind AlpineJS Laravel Livewire).

Cas particulier : Laravel

Si vous utilisez Laravel, c'est maintenant installé par défaut. Si vous voulez le voir comme un avantage, ça ne me pose aucun problème. Il n'y a pas de problème avec Laravel pour l'installation et la configuration de Tailwind CSS, c'est de base.

Par contre si tu installes Laravel Jetstream et que tu ne veux pas utiliser Tailwind CSS, tu pleures car t'es bon pour repasser sur 5/6 templates de page et 28 composants blade.

Une prise en main rapide

Tailwind CSS est facile à prendre en main. Les classes sont intuitives, la documentation semble plutôt complète. Il y a quelques propriétés CSS qui ne sont pas gérées comme column-count: 2; mais c'est rare.

Il y a aussi une palette de couleurs conséquente avec 8 couleurs et 10 déclinaisons pour chaque couleur. En réalité, il y a même une grosse vingtaine de couleurs si on compte les déclinaisons de gris, de bleu, etc.

Après l'installation de Tailwind CSS, on a plus qu'à rajouter nos classes sur nos éléments HTML et ça fonctionne. Par exemple, je vais commencer en mettant mon code html et des classes de base de Tailwind :

<h1 class="font-bold text-3xl text-green-400 mb-8 py-3 border-b border-green-600">Mon titre de niveau 1</h1>

<p class="text-sm text-gray-900">Paragraphe d'intro</p>

<h2 class="font-bold text-2xl text-green-600 mb-4 py-3 border-b border-gray-300">Mon titre de niveau 2</p>

<p>Paragraphe de blabla</p>

Ça ne fonctionne déjà pas. Par défaut, Tailwind reset tout le CSS, il n'y a donc plus aucun style sur les balises. Ici, j'ai mis du "margin bottom" sur les Hx (mb-8 et mb-4) mais pas sur les paragraphes. Donc mon h2 est collé à mon texte d'intro mais peu importe.

J'ai un aperçu rapide du design, je peux ajuster le margin bottom du h1 en remplaçant mb-8 par mb-16 pour essayer, ou encore foncer un peu le titre en passant de text-green-400 à text-green-600. J'avance petit à petit et encore une fois c'est facile, je n'ai pas besoin de beaucoup réfléchir. Je ne sais pas à quelle taille correspond text-3xl ou mb-8 mais je n'ai pas besoin de savoir, j'augmente ou je diminue en fonction de mon besoin. Quand tu ne sais pas trop où tu vas en design, c'est top.

Pas de problème de poids du sélecteur

En prenant en compte l'utilisation la plus simple de Tailwind CSS, c'est-à-dire ajouter des classes à chaque élément, je n'ai pas besoin de connaître le poids des sélecteurs CSS. C'est ce qui pose généralement problème lorsqu'on a pas l'habitude du CSS.

Pourquoi je mets mon texte en rouge mais il reste bleu ?

Prototypage rapide

On revient sur la cible principale du framework, les dev backend. Je veux créer une landing page mais je n'ai pas défini de design, avec Tailwind CSS, je n'ai pas besoin de passer par un outil de maquettage. Je monte le HTML de ma page, j'ajoute des classes et j'ajuste au fur et à mesure, jusqu'à avoir un design de page qui me convient.

La performance

Tailwind CSS vient avec un système de purge pour supprimer toutes les classes du fichier CSS qui ne sont pas utilisées dans nos templates. Ceci réduit grandement le poids du fichier pour éviter de charger des classes inutiles sur son site.

Pas grand chose à dire de plus dessus, c'est un faux avantage d'après moi, j'en parle plus bas.

On passe maintenant à la partie qui m'intéresse, je vais pouvoir me faire plaisir : les problèmes que j'ai constaté avec ce framework !

Le code devient illisible

L'ajout de classes dans les templates HTML devient très vite un problème selon moi. Je vais prendre l'exemple du design d'un simple bouton.

Je veux un bouton bleu avec le texte en blanc, sur mobile le bouton doit prendre toute la largeur et au survol ou au focus, le bouton passe blanc avec le texte en bleu et une bordure bleu. J'ajoute un peu de padding pour avoir de l'espace autour du texte du bouton.

En vanilla css mon code va ressembler à ça :

<button type="submit" class="button-primary">Envoyer ma demande</button>

<style>  
.button-primary {  
    color: white;  
    background: blue;  
    border: 1px solid transparent;  
    width: 100%;  
    padding: .75rem 1.5rem;  
    transition: all .5s ease;    
}  

.button-primary:hover,  
.button-primary:focus {  
    color: blue;  
    background: white;  
    border-color: blue;  
}  

@media (min-width: 768px) {  
    .button-primary {  
        width: inherit;  
    }  
}  
</style>

Avec Tailwind je n'ai pas besoin de CSS autre que Tailwind lui même. Mon code ressemblera à ça :

<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">  
<button  
    class="block w-full md:w-max text-white bg-blue-500 border border-transparent px-8 py-4 transition focus:text-blue-500 focus:bg-white focus:border-blue-500 hover:text-blue-500 hover:bg-white hover:border-blue-500"  
>  
    Envoyer ma demande  
</button>

Si je veux rajouter des états supplémentaires je dois encore rajouter des classes. Par exemple je peux rajouter active:border-red-500 pour que la bordure soit rouge juste au moment du clic.

Du coup, je peux comprendre que le code CSS du premier exemple n'est pas forcément évident quand il faut l'écrire. Si on ne connaît pas bien, ça demande de la réflexion et c'est plus simple de faire la version Tailwind. Seulement, à ce niveau, je trouve déjà le bouton illisible avec toutes ses classes. Si on a ça sur les div, ul, p , Hx, on ne distingue même plus le html.

Ici, j'ai volontairement changé qu'une seule chose en mobile. Sinon, c'est comme le hover et le focus, il fallait rajouter une classe pour chaque chose qui change aux différents breakpoint. Si on change le padding gauche et droite pour en mettre moins sur mobile, il faudrait écrire px-4 md:px-8, si je veux ajuster le padding pour chaque breakpoint, je dois écrire px-4 sm:px-6 md:px-8 lg:px-10 xl:px-16. Je veux aussi ajuster le padding en haut et en bas de l'élément pour chaque breakpoint ? Quatre classes supplémentaires, une pour chaque breakpoint.

Si on a décidé de partir avec Tailwind, à ce moment on a plusieurs solutions :

  1. Garder ce code en l'état ce qui nous obligerait à copier/coller toutes les classes du bouton à chaque fois que je veux l'utiliser. Le problème : très vite, j'ai des boutons différents sur chaque page, avec un padding ou un margin différent, une déclinaison de couleur qui change car j'ai mis bg-blue-400 au lieu de bg-blue-500.
  2. Le sortir dans un composant si on utilise Laravel, c'est la "bonne pratique" du point de vue de Laravel. Mais je ne peux pas overwrite certaines classes sur le composant. Donc, si je veux pouvoir ajuster le margin top en fonction du bouton, je ne dois pas mettre de marge haute sur le composant, et la rajouter dans mon template HTML pour chaque bouton. On revient alors à ma première solution, où je n’ai plus aucune unité sur mon site car je dois récupérer les marges d'un autre bouton pour essayer d'avoir les mêmes.
  3. Extraire mes classes et les ajouter dans mon css. Mais là on retombe sur des problèmes liés au CSS pur : le poids des sélecteurs. Si je ne connais pas bien le CSS, ça ne va pas fonctionner comme je veux.

Voilà le rendu façon Tailwind avec ce que je considère comme la bonne pratique.

<button type="submit" class="button-primary">Envoyer ma demande</button>  

<style>
.button-primary {
    @apply block;  
    @apply w-full;  
    @apply text-white;  
    @apply bg-blue-500;  
    @apply border border-transparent;  
    @apply px-8 py-4;  
    @apply transition;  
}

.button-primary:hover,  
.button-primary:focus {  
    @apply text-blue-500;  
    @apply bg-white;  
    @apply border-blue-500;  
}

@screen md {  
    .button-button {  
        @apply w-max;  
    }  
}

J'ai finalement à peu de chose près, le même code que mon bouton en CSS pur.

La documentation ne pousse pas à la bonne pratique

La bonne pratique consiste à sortir toutes ces classes du HTML pour les remettre dans son fichier css avec des @apply.

Du coup l'autre problème qui me saute aux yeux : ce n'est utilisé nulle part dans la doc !

Ils ont une page dédiée dans leur documentation mais ils ne l'appliquent jamais dans les exemples.

Même Tailwind UI qui vend un accès à des composants "préfabriqués" pose toutes les classes dans le HTML.

Si je transpose ça au CSS pur : On te dit comment ajouter du style directement dans les balises HTML de ton template mais on t'explique que tu ne devrais pas faire comme ça, et les déclarer dans un fichier CSS dédié. Je ne trouve pas ça logique du tout.

Si je prends l'exemple de mon bouton bleu je peux très bien l'écrire comme ça :

<!-- CSS en dur -->
<button type="submit"
        style="color: white; background: blue; border: 1px solid transparent; width: 100%; padding: .75rem 1.5rem; transition: all .5s ease;">
    Envoyer ma demande
</button>

<-- Tailwind -->
<button class="w-full text-white bg-blue-500 border border-transparent px-8 py-4 transition">
    Envoyer ma demande
</button>

Ces deux lignes sont identiques et font la même chose. Seulement si tu vois la première dans ton code, tu assassines le développeur.

On complique son code pour ne pas apprendre le CSS

Je vais prendre l'exemple des développeurs Laravel car j'ai vu quelques cas qui m'ont rendu fou. Comme Tailwind CSS te permet de ne pas faire de CSS, on va chercher une solution compliquée en code pour un problème qui peut se gérer en 3 lignes en CSS.

L'exemple le plus flagrant, qui ne sera plus actif longtemps car Tailwind CSS a sorti deux nouveaux états pour ça : odd et even.

Je veux un tableau HTML avec une couleur de fond qui change une ligne sur deux, voici le lien vers la doc Tailwind des tableaux.

Ils ajoutent la classe bg-emerald-200 une ligne sur deux. Sans connaître le CSS, je dois donc ajouter dans mon template une classe sur ma balise <tr> une fois sur deux.

Voici une solution trouvée sur le forum Laracasts :

@foreach ($lignes as $index => $row)
@if ($index % 2)
<tr>
    ...
</tr>
@else
<tr class="bg-emerald-200">
    ...
</tr>
@endif
@endforeach

Ce code est à appeler dans notre template Blade. Qu'est ce qu'il fait ? Il regarde l'index (2e ligne du tableau, 3e ligne, 4e ligne, etc.) et si l'index est divisible par 2, cela veut dire qu'on est sur une ligne pair, on affiche notre ligne normale. Sinon, on est sur une ligne impair, on ajoute notre classe bg-emerald-200.

En CSS il est possible de cibler les éléments pair et impair. Voilà la solution en CSS, en utilisant Tailwind CSS en plus !

tr:nth-child(odd) {
    @apply bg-emerald-200;
}

C'est très facile en CSS, ça n'implique pas de modifier son template, c'est faisable même en utilisant Tailwind CSS. Seulement comme je n'ai pas besoin d'apprendre le CSS, je ne vais pas le savoir et je vais faire un code sale, qui va compliquer la lecture pour le développeur, qui va m'obliger à modifier deux lignes différentes si je veux ajouter du padding aux <tr>, je complexifie tout.

Deuxième exemple bien plus rapide : je ne veux pas de marges hautes sur une liste à puce qui suit directement un paragraphe. Si j'ajoute des classes Tailwind CSS dans mon template HTML, je dois manuellement mettre une classe mt-0 à la liste qui m'intéresse. Demain j'enlève le paragraphe du dessus, si je ne pense pas à enlever ma classe mt-0 sur la liste du dessous, je me retrouve avec une liste à puce collée au titre.

La solution en CSS :

/** Vanilla CSS **/
p + ul {
    margin-top: 0;
}

/** Tailwind CSS **/
p + ul {
    @apply mt-0;
}

Du code difficilement maintenable dans le temps

Cela rejoint le premier problème, on a beau dire qu'il faut extraire les classes dans son fichier CSS, on sait très bien qu'une fois sur deux on ne va pas le faire. Et si on veut le faire, finalement cela devient du CSS pur dépendant de Tailwind pour fonctionner.

Je me retrouve alors avec des templates HTML qui deviennent complètement illisibles, qui vont être facile à écrire sur le moment mais difficile à lire et modifier plus tard.

Voici un exemple de modale sur tailwind UI. On peut voir le code HTML sans le JS qui permet d'ouvrir ou fermer la modale.


<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
    <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
        <div class="sm:flex sm:items-start">
            <div class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
                <!-- Heroicon name: outline/exclamation -->
                <svg class="h-6 w-6 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
                     stroke="currentColor" aria-hidden="true">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                          d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
                </svg>
            </div>
            <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
                <h3 class="text-lg leading-6 font-medium text-gray-900" id="modal-title">
                    Deactivate account
                </h3>
                <div class="mt-2">
                    <p class="text-sm text-gray-500">
                        Are you sure you want to deactivate your account? All of your data will be permanently removed.
                        This action cannot be undone.
                    </p>
                </div>
            </div>
        </div>
    </div>
    <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
        <button type="button"
                class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
            Deactivate
        </button>
        <button type="button"
                class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
            Cancel
        </button>
    </div>
</div>

Le bouton "Cancel" prend quasiment 2 écrans en largeur sur mon 27". Maintenant, imagine avoir tout ton code HTML comme ça. Six mois plus tard, tu veux modifier le padding de ta modale, combien de temps faut-il pour trouver quoi changer ? En espérant quand même que tu ais fait l'effort de sortir cette modale dans un fichier à part, sinon tu dois chercher toutes tes modales dans ton code pour toutes les modifier.

Je reviens maintenant sur l'avantage de Tailwind CSS par rapport aux autres frameworks, la performance.

Tailwind se sert de PurgeCSS. Il y a une documentation claire sur le site pour l'utiliser avec webpack, gulp, grunt, etc. Le code ressemble à la configuration à utiliser dans Tailwind pour la purge, c'est normal.

Si vous en doutez, voilà ce que dit la documentation Tailwind :

PurgeCSS (the library we use under the hood) is intentionally very naive in the way it looks for classes in your HTML.
It doesn’t try to parse your HTML and look for class attributes or dynamically execute your JavaScript — it simply looks for any strings in the entire file

Si c'est LA raison qui te pousse à changer de framework, je ne comprends pas. Dans ce cas, tu passeras moins de temps à ajouter cette lib PurgeCSS sur ton framework actuel.

Un petit point sur les variables car on m'a dit sur Twitter, qu'avec du CSS pur, si je décidais de changer la couleur principale par exemple, je devrais modifier tout mon fichier CSS.

Pour le cas particulier des couleurs, même à l'arrache, un rechercher/remplacer dans un dossier peut convenir.

Il faut savoir que les variables existent en Sass et même en CSS pur !

Tailwind CSS lui-même utilise les variables CSS pour définir ses classes.

(Didier) C'est moi qui suis tombé amoureux de TailwindCSS à sa sortie. Poussé par l'écosystème Laravel, le framework promet de simplifier la gestion des CSS. J'aime toujours les efforts que font ses créateurs pour aider les développeurs à prendre en mains le design de leurs sites (via des vidéos, tutoriaux, bonnes pratiques, ateliers, etc). Par contre, je rejoins l'avis d'Étienne sur le côté technique : Tailwind incite à déclarer des tonnes de classes dans son HTML, ce qui accélère la phase de prototypage mais demande plus d'efforts sur le long terme quand on veut optimiser les process de l'entreprise.

On bidouille souvent le CSS car c'est facile mais on peut se retrouver bloqué si on ne possède pas les notions de base. D'après moi, il n'y a que deux notions essentielles à comprendre en CSS.

Les sélecteurs

Pour commencer, il faut comprendre ce qu'est un sélecteur et les différentes possibilités. Vous pouvez avoir la liste sur W3schols par exemple.

Avec ces sélecteurs on peut presque tout cibler :

  • .intro > p => Cible les paragraphes qui sont directement enfants d'une classe .intro.
  • .intro + p => Cible le paragraphe qui suit directement la fermeture d'une classe .intro.
  • :not(selecteur) => Inverse le sélecteur, si je peux dire ça comme ça.
  • a:not([href^="https://soumettre.fr"]) => Cible tous les liens dont l'attribut href ne commence pas par "https://soumettre.fr".
/** Je passe tous les liens dont le href ne commence pas par "https://soumettre.fr" en rouge **/
a:not([href^="https://soumettre.fr"]) {
    color: red;
}

/** Je rajoute le texte " (lien externe)" après les liens qui ne pointent pas vers https://soumettre.fr **/
a:not([href^="https://soumettre.fr"])::after {
    content: " (lien externe)";
}

Pour faire simple, j'imagine que vous avez tous des extensions sur votre navigateur pour encadrer les images avec un alt vide, vous afficher le title des liens, encadrer les liens nofollow...Ces extensions appliquent simplement du CSS supplémentaires sur la page que vous consultez.

Du style en cascade

Le CSS s'applique par ordre de priorité, chaque sélecteur a un poids. Le poids le plus faible s'applique en premier puis il est écrasé par les propriétés d'un sélecteur avec un poids plus fort.

Le calcul du poids est très simple :

  • Une balise HTML = 1 point
  • Une classe = 10 points
  • Un identifiant = 100 points

C'est simplifié, et je ne suis même pas certain qu'un selecteur avec 10 balises HTML dedans sois équivalent à un sélecteur d'une classe, mais tu comprends l'idée.

Le sélecteur .main-content p a un poids de 11 tandis que le sélecteur p a un poids de 1.

En considérant le code suivant :

p {
    margin-bottom: 20px;
    font-size: 16px;
    color: black;
}

.main-content p {
    font-size: 22px;
}

Les paragraphes dans la classe .main-content auront les propriétés du paragraphe normal mais le font-size: 22px va prendre le dessus sur les 16px du paragraphe par défaut.

C'est la raison pour laquelle on ajoute parfois du CSS mais on ne voit aucune modification réelle en front, tout est une histoire de priorité.

Voici un schema qui explique complètement le poids du sélecteur.

Je pense que Tailwind CSS peut être un atout lorsqu'on ne connait pas du tout le CSS et qu'on ne {veut|peut} pas s'y mettre. Il est aussi utile si on veut lancer un petit projet rapidement qui n'est pas voué à évoluer.

Si ce projet évolue, il faudra peut-être envisager une refonte pour sortir Tailwind CSS. D'après moi, le temps gagné au début du projet ne compense pas le temps perdu ensuite pour le maintenir.

Si la raison du changement est la performance, prends plutôt le temps de mettre en place PurgeCSS sur le framework que tu utilises, ton thème WordPress, ou peu importe.

Pour ma part si je devais choisir, je préférerais partir sur du SCSS avec gulp qui me permettra de :

  • build le CSS,
  • le purger si je le souhaite,
  • le minifier,
  • ajouter les prefixes navigateur...

Mais au moins je me retrouve avec un langage de programmation pour du CSS.

Je suis preneur de vos avis, comme je le disais au début j'ai vraiment l'impression de passer à côté de quelque chose avec ce framework. Il semble à la mode, tout le monde l'adore, mais vraiment j'arrive pas à comprendre pourquoi, ni l'avantage qu'il a.

Laisser un commentaire

1 commentaire

Aurélien Denis
Aurélien Denis 16/01/2022

Je valide à 100% cette analyse ! J'aurais aimé connaître votre avis sur le poids du DOM en SEO qui ne passera aucun test PageSpeed.

Pour ma part, j'ai adopté depuis bien longtemps le même processus de développement front que celui recommandé en conclusion : SCSS + Gulp.

NewsletterVeille SEO

Recevez notre veille SEO par email, une fois par semaine.