Développement Web

Tailwind utility-first et responsive : méthode pas à pas

13 min de lecture

La première fois qu’on découvre Tailwind, une réaction revient presque toujours : « mais c’est sale, tout ce style dans le HTML ! » Cette objection mérite une vraie réponse, parce qu’elle masque le changement de modèle mental le plus important du framework. Écrire class="flex items-center gap-4 p-4" n’est pas une régression vers les styles en ligne des années 2000 : c’est une autre façon de penser le CSS, où l’on compose des interfaces à partir de petites briques contraintes plutôt que d’inventer un nom de classe pour chaque élément. À la fin de ce tutoriel, vous maîtriserez cette logique utility-first et vous saurez rendre n’importe quel composant de StockLab adaptatif, du téléphone au grand écran.

🎯 Ce que vous allez apprendre

  • Expliquer ce qu’est l’approche utility-first et pourquoi elle réduit la dette CSS sur le long terme.
  • Composer une interface entière sans écrire une seule règle CSS personnalisée.
  • Rendre un composant responsive avec les préfixes de point de rupture sm:, md:, lg:.
  • Cibler des plages d’écran précises avec les variantes max-* et gérer les états comme hover: et focus:.

🛠️ Ce que vous allez construire

Nous reprenons StockLab et nous construisons sa barre supérieure et une fiche produit. Sur mobile, la fiche s’empile verticalement et occupe toute la largeur ; sur tablette et au-delà, elle bascule en disposition horizontale avec une image à gauche et les informations à droite. Tout cela uniquement avec des classes utilitaires, sans toucher à un fichier CSS.

Prérequis

  • Un projet Tailwind v4 fonctionnel (voir le tutoriel d’installation si ce n’est pas encore fait).
  • Des bases solides en HTML : balises, attributs, structure d’un document.
  • Test express : si vous savez écrire un <div> contenant une image et un titre, vous êtes prêt.
  • ⏱️ Temps estimé : environ 30 minutes.

Comprendre l’idée utility-first avant d’écrire la moindre classe

Dans l’approche classique, vous écrivez du HTML sémantique puis vous lui attachez une feuille de styles : .product-card { … }, .product-title { … }, et ainsi de suite. Le problème surgit avec le temps. Vos noms de classes deviennent des décisions irréversibles ; vous n’osez plus toucher à .card de peur de casser un écran que vous avez oublié ; et votre fichier CSS gonfle sans fin parce que chaque nouvelle page ajoute ses propres règles. C’est la dette CSS : invisible au début, paralysante au bout d’un an.

L’approche utility-first renverse la logique. Au lieu de nommer des composants, vous appliquez directement des classes à usage unique et prévisible : p-4 met un rembourrage, flex active flexbox, text-lg agrandit le texte. Chacune fait une seule chose, et son nom décrit exactement son effet. Trois conséquences en découlent. D’abord, vous n’inventez plus de noms : la fatigue du nommage disparaît. Ensuite, votre CSS cesse de grandir : deux composants qui utilisent p-4 partagent la même règle, générée une seule fois. Enfin, vous travaillez dans des limites saines : l’échelle d’espacement de Tailwind vous évite les valeurs aléatoires comme 13px ou 27px qui rendent une interface incohérente.

La crainte du « HTML encombré » est réelle mais surmontable. La réponse de Tailwind tient en deux temps : pour la répétition à l’intérieur d’une page, on boucle sur les données plutôt que de copier le markup ; pour la réutilisation entre pages, on extrait un composant — un partial, un composant React ou Vue — qui encapsule la liste de classes. Vous gardez ainsi les avantages de l’utility-first sans dupliquer.

Étape 1 — Une première barre supérieure

Commençons par l’en-tête de StockLab : un logo à gauche, un titre, et un bouton à droite. L’outil ici est flexbox, activé par la classe flex. On aligne verticalement le contenu et on pousse le bouton à l’extrémité opposée.

<header class="flex items-center justify-between bg-white p-4 shadow-sm">
  <span class="text-xl font-bold text-indigo-600">StockLab</span>
  <button class="rounded-md bg-indigo-600 px-4 py-2 text-white">
    Ajouter un article
  </button>
</header>

Décortiquons. flex place les enfants sur une ligne ; items-center les centre verticalement ; justify-between répartit l’espace pour coller le logo à gauche et le bouton à droite. Le reste habille : p-4 donne du rembourrage, shadow-sm une ombre discrète, rounded-md arrondit les angles du bouton. À l’écran, vous obtenez une barre nette et professionnelle sans avoir nommé quoi que ce soit.

Point d’étape — Votre en-tête affiche le logo à gauche et le bouton à droite, parfaitement alignés. Si le bouton ne se décale pas à droite, vérifiez que justify-between est bien sur le conteneur flex et non sur un enfant.

Étape 2 — La fiche produit en disposition mobile

Place à la fiche d’un article de stock. On commence toujours par le plus petit écran — c’est le principe du mobile-first. Sur téléphone, l’image se place au-dessus, le texte en dessous ; tout s’empile dans une colonne.

<article class="flex flex-col gap-4 rounded-lg bg-white p-4 shadow">
  <img src="/visseuse.jpg" alt="Visseuse sans fil" class="w-full rounded-md">
  <div>
    <h2 class="text-lg font-semibold text-gray-900">Visseuse sans fil</h2>
    <p class="text-sm text-gray-500">Réf. SL-204 — 37 en stock</p>
  </div>
</article>

flex flex-col empile les enfants verticalement ; gap-4 insère un espace régulier entre l’image et le texte sans marges fragiles. L’image prend toute la largeur avec w-full. Cette version mobile est complète et lisible telle quelle : c’est notre socle, et on ne va l’enrichir que pour les écrans plus larges.

Étape 3 — Rendre la fiche responsive avec les points de rupture

Voici le cœur du sujet. En Tailwind, une classe sans préfixe s’applique à toutes les tailles d’écran. Un préfixe comme md: ne s’active qu’à partir d’une largeur donnée et au-delà. C’est le mobile-first : on écrit le style de base pour le petit écran, puis on surcharge pour les grands. Faisons passer notre fiche en disposition horizontale dès la tablette.

<article class="flex flex-col gap-4 rounded-lg bg-white p-4 shadow md:flex-row md:items-center">
  <img src="/visseuse.jpg" alt="Visseuse sans fil" class="w-full rounded-md md:w-40">
  <div>
    <h2 class="text-lg font-semibold text-gray-900">Visseuse sans fil</h2>
    <p class="text-sm text-gray-500">Réf. SL-204 — 37 en stock</p>
  </div>
</article>

Trois classes préfixées font tout le travail. md:flex-row bascule l’empilement vertical en disposition horizontale à partir de 768 pixels ; md:items-center centre alors verticalement l’image et le texte ; md:w-40 fixe une largeur d’image raisonnable pour qu’elle ne dévore pas la ligne. En dessous de 768 pixels, ces règles n’existent pas et la version mobile empilée reste en place. Redimensionnez la fenêtre du navigateur : vous voyez la fiche basculer d’un mode à l’autre au franchissement du seuil.

Point d’étape — En élargissant la fenêtre au-delà de 768 pixels, la fiche passe en disposition horizontale ; en la rétrécissant, elle revient à la colonne. Si rien ne change, c’est souvent que la largeur de fenêtre n’a pas franchi le seuil md — testez avec les outils de développement en mode appareil mobile.

Les points de rupture par défaut

Tailwind fournit cinq points de rupture standard, tous fondés sur des largeurs minimales. Les connaître par cœur fait gagner un temps précieux.

Préfixe Largeur minimale Cible typique
sm: 40rem (640 px) Grands téléphones, petites tablettes
md: 48rem (768 px) Tablettes
lg: 64rem (1024 px) Ordinateurs portables
xl: 80rem (1280 px) Écrans de bureau
2xl: 96rem (1536 px) Grands écrans

Ces valeurs sont exprimées en rem, ce qui les rend solidaires de la taille de police de l’utilisateur — un détail d’accessibilité souvent négligé. Si vos besoins diffèrent, vous pouvez ajouter un point de rupture personnalisé dans le bloc @theme avec une variable comme --breakpoint-3xl: 120rem;, et Tailwind générera aussitôt le préfixe 3xl: correspondant.

Étape 4 — Cibler une plage précise avec les variantes max

Le mobile-first couvre « à partir de telle taille ». Mais parfois on veut l’inverse : appliquer un style uniquement en dessous d’un seuil, ou seulement entre deux seuils. Les variantes max-* répondent à ce besoin. Supposons qu’on veuille masquer la référence produit sur les très petits écrans pour gagner de la place.

<p class="text-sm text-gray-500 max-sm:hidden">Réf. SL-204 — 37 en stock</p>

max-sm:hidden cache l’élément tant que la fenêtre fait moins de 640 pixels, puis le révèle au-delà. On peut aussi combiner deux variantes pour viser une plage : md:max-lg:flex n’active flexbox qu’entre 768 et 1024 pixels. Cette précision évite les bricolages et garde la logique lisible directement dans le markup.

Étape 5 — Gérer les états interactifs

Une interface vivante réagit au survol, au focus, au clic. Tailwind exprime ces états avec des variantes préfixées, exactement comme les points de rupture. Donnons un retour visuel au bouton « Ajouter un article ».

<button class="rounded-md bg-indigo-600 px-4 py-2 text-white transition hover:bg-indigo-700 focus:outline-2 focus:outline-indigo-400">
  Ajouter un article
</button>

hover:bg-indigo-700 assombrit le fond au survol ; focus:outline-2 et focus:outline-indigo-400 dessinent un contour net quand le bouton reçoit le focus clavier — un point d’accessibilité essentiel pour la navigation sans souris. La classe transition adoucit le passage d’un état à l’autre. Ces variantes se cumulent à l’infini : on peut écrire md:hover:scale-105 pour un effet qui ne joue qu’au survol sur les écrans moyens. C’est cette composabilité qui rend l’utility-first si expressif.

Quand les valeurs standard ne suffisent pas

L’échelle de Tailwind couvre l’immense majorité des besoins, mais il arrive qu’un design impose une valeur hors grille — une largeur de 220 pixels imposée par une maquette, par exemple. Plutôt que d’écrire du CSS à côté, vous utilisez la notation entre crochets : w-[220px], top-[37px], bg-[oklch(0.7_0.15_30)]. Cette valeur arbitraire reste intégrée au système : elle accepte les variantes (md:w-[220px]) et garde votre style centralisé dans le HTML. À utiliser avec parcimonie : si vous répétez la même valeur arbitraire partout, c’est le signe qu’elle mérite un token dans @theme.

🐞 Pièges fréquents

Symptôme / erreur Cause probable Correctif
Un style mobile s’applique aussi sur grand écran alors qu’on ne le voulait pas Confusion mobile-first : la classe sans préfixe vaut pour toutes les tailles Mettre le style de base pour mobile, surcharger avec md: ou lg: pour les grands écrans
sm:text-center ne centre rien sur téléphone sm: ne s’active qu’à partir de 640 px, pas en dessous Écrire text-center sans préfixe pour le mobile
Le survol ne fonctionne pas sur mobile Les écrans tactiles n’ont pas d’état hover fiable Ne jamais cacher une information essentielle derrière hover: seul
Une valeur arbitraire avec espace casse la classe Les espaces ne sont pas permis dans les crochets Remplacer l’espace par un tiret bas : bg-[oklch(0.7_0.15_30)]

✅ Récapitulatif

Vous avez basculé dans la pensée utility-first. Vous savez désormais qu’une classe sans préfixe vaut pour tous les écrans et qu’un préfixe de point de rupture surcharge à partir d’un seuil ; vous avez construit un en-tête et une fiche produit qui s’adaptent du téléphone au bureau ; vous maîtrisez les variantes max-* pour cibler des plages, les états hover: et focus: pour l’interactivité, et les valeurs arbitraires pour les cas hors grille. StockLab a maintenant une interface réellement responsive, sans une ligne de CSS personnalisé.

🧾 Aide-mémoire

Classe / notation Effet
flex flex-col / md:flex-row Empiler en colonne, passer en ligne dès md
items-center justify-between Aligner verticalement, répartir aux extrémités
gap-4 Espace régulier entre enfants flex ou grid
max-sm:hidden Masquer en dessous de 640 px
hover:bg-indigo-700 Style appliqué au survol
w-[220px] Valeur arbitraire hors échelle standard

💪 À vous de jouer

Faites en sorte que le titre de la fiche produit soit de taille text-lg sur mobile et text-2xl à partir des écrans lg. Bonus : ajoutez un effet d’agrandissement léger de toute la fiche au survol, mais seulement sur les écrans de bureau.

Voir une solution
<h2 class="text-lg font-semibold text-gray-900 lg:text-2xl">Visseuse sans fil</h2>

<article class="… transition lg:hover:scale-[1.02]">…</article>

lg:text-2xl n’agrandit le titre qu’à partir de 1024 pixels ; lg:hover:scale-[1.02] combine point de rupture et état pour un effet réservé au bureau, où le survol a du sens.

Tutoriels frères

Ressources et références

FAQ

Q : Mettre autant de classes dans le HTML, n’est-ce pas mauvais pour la performance ?
R : Non. Le navigateur lit du HTML ; la longueur des attributs class n’a pas d’impact mesurable. Au contraire, le CSS livré est minuscule car les utilitaires se partagent. La vraie économie est là.

Q : Comment éviter de répéter les mêmes longues listes de classes ?
R : Deux solutions : boucler sur des données pour les répétitions au sein d’une page, et extraire un composant (partial, composant React/Vue) pour la réutilisation entre pages. La directive @apply existe aussi pour les cas particuliers.

Q : Pourquoi commencer par le mobile plutôt que par le bureau ?
R : Parce que le style de base sans préfixe s’applique partout ; partir du plus simple (mobile) et ajouter de la complexité vers le haut produit moins de surcharges et un code plus clair que l’inverse.

Q : Les valeurs en rem des points de rupture, est-ce important ?
R : Oui pour l’accessibilité : en rem, les seuils suivent la taille de police choisie par l’utilisateur, ce qui respecte ses préférences de lisibilité.

Partager