Premiers pas avec Tailwind
Objectif : mettre les mains dedans. Cinq exercices qui partent du code tout fait et te lâchent progressivement la main, jusqu'à ce que tu conçoives tes propres composants. Deux d'entre eux finiront dans ton portfolio.
Comment lire ce chapitre
Chaque exercice est découpé en paliers. Tu ne fais pas tous les paliers de tous les exercices au hasard : tu suis l'ordre.
| Palier | Ce que tu fais | Ce que tu y gagnes |
|---|---|---|
| 1 — Copie et observe | Tu colles le code donné, puis tu modifies une ou deux classes imposées et tu regardes le résultat | Tu vois ce que fait chaque classe, sans réfléchir à la mise en page |
| 2 — Complète les trous | Le code est là, avec des ??? à remplacer. Chaque trou est précédé d'un commentaire qui dit ce qu'on attend et propose trois candidates |
Tu choisis, donc tu décides. Plusieurs réponses sont justes |
| 3 — À toi | Une consigne, des critères, une banque de classes. Pas de code | Tu conçois |
Au palier 2, plusieurs réponses sont correctes. Les trois candidates proposées fonctionnent toutes techniquement : la question est de savoir laquelle sert ton intention. C'est exactement le travail du reste de l'année.
Astuce
Garde l'autocomplétion de Tailwind CSS IntelliSense ouverte pendant que tu remplis les trous : tape les premières lettres d'une candidate, VS Code te montre la couleur et la valeur CSS derrière. C'est plus rapide que la doc.
Setup
Ouvre src/App.vue et remplace tout son contenu par ceci. Toutes les sections des exercices viendront se poser à l'intérieur du <main>, les unes sous les autres.
<script setup>
// Rien ici : on ne fait que du template et des classes.
</script>
<template>
<main class="min-h-screen bg-stone-100 p-8">
<h1 class="text-4xl font-bold tracking-tight text-zinc-900 mb-10">
Mes exercices Tailwind
</h1>
<!-- Tes sections d'exercices viennent ici -->
</main>
</template>
Lance le serveur si ce n'est pas déjà fait :
npm run dev
Et ouvre http://localhost:5173.
Exercice 1 — Boutons
Durée indicative : 45 minutes.
Palier 1 — Copie et observe
Colle cette section dans ton <main> :
<section class="mb-12">
<h2 class="text-2xl font-bold text-zinc-900 mb-4">Boutons</h2>
<div class="flex flex-wrap gap-4">
<button class="bg-zinc-900 text-stone-50 px-5 py-2.5 rounded-full font-medium hover:bg-zinc-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-900 transition">
Me contacter
</button>
</div>
</section>

Maintenant, trois manipulations. Fais-les une par une, regarde le navigateur entre chaque, puis remets la classe d'origine avant de passer à la suivante.
- Remplace
bg-zinc-900parbg-amber-300. Le bouton devient jaune… et le textetext-stone-50devient presque illisible. C'est un problème de contraste. Corrige en mettant aussitext-zinc-900. - Supprime
rounded-full. Le bouton devient un rectangle à angles droits. Essaie ensuiterounded-md, puisrounded-none. Trois personnalités très différentes pour une seule classe. - Supprime
transition. Survole le bouton : le changement de couleur devient brutal, instantané. Remets-la.
Note
Prends l'habitude de cette boucle : je change une classe, je regarde, je comprends, je passe à la suivante. Changer cinq classes d'un coup et se demander laquelle a fait quoi, c'est comme ça qu'on perd une heure.
Palier 2 — Complète les trous
Colle ce deuxième bouton dans le même <div class="flex flex-wrap gap-4">. Remplace chaque ??? par une classe. Le commentaire juste au-dessus te dit ce qu'on attend et te propose trois candidates : choisis-en une.
<button class="
??? ??? ??? ??? ??? ??? ??? ???
">
Voir mes projets
</button>
Voici les huit trous, dans l'ordre :
<!-- ??? couleur de fond : bg-amber-300 | bg-emerald-400 | bg-orange-500 -->
<!-- ??? couleur du texte, qui doit contraster avec le fond choisi : text-zinc-900 | text-white | text-stone-50 -->
<!-- ??? padding intérieur, généreux : px-6 py-3 | px-8 py-4 | px-4 py-2 -->
<!-- ??? bordure épaisse et noire : border-2 border-zinc-900 | border-4 border-zinc-900 | border-8 border-zinc-900 -->
<!-- ??? arrondi : rounded-none | rounded-sm | rounded-md -->
<!-- ??? ombre dure et décalée : shadow-[4px_4px_0_0_#18181b] | shadow-[6px_6px_0_0_#18181b] | shadow-lg -->
<!-- ??? effet au survol : hover:-translate-y-0.5 | hover:bg-zinc-900 | hover:rotate-1 -->
<!-- ??? focus clavier visible : focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-900 | focus-visible:ring-2 focus-visible:ring-zinc-900 | focus-visible:ring-4 focus-visible:ring-amber-300 -->
Objectif : un bouton brutaliste — couleur franche, bordure noire épaisse, angles droits ou presque, ombre dure. Pas de flou, pas de dégradé.
Teste avec la touche [TAB] : ton bouton doit montrer clairement qu'il a le focus.
Palier 3 — À toi
Consigne : ajoute trois boutons de plus, chacun dans une direction esthétique différente, choisie dans cette liste :
- éditorial : serif, fin, beaucoup d'air, peu de couleur ;
- riso / imprimé : une couleur franche, une bordure épaisse, aucun effet de profondeur ;
- minimal japonais : blanc cassé, trait fin, une seule respiration ;
- brutaliste : contrastes violents, majuscules, ombre dure ;
- ticket de caisse : monospace, majuscules, bordure pointillée.
Trois directions différentes : pas trois variantes de la même. Le but est que le prof puisse deviner ta direction sans que tu la nommes.
Attention
Interdits, et ce n'est pas négociable : néon / cyberpunk, glassmorphism (backdrop-blur + fond blanc translucide), dégradés violet-rose. C'est le slop du chapitre d'intro : le réglage par défaut de tous les générateurs, donc l'absence de décision. Tu peux le faire pour rire chez toi, pas ici.
Banque de classes — pioche, combine, cherche le reste dans la doc.
| Catégorie | Classes |
|---|---|
| Fond | bg-white, bg-stone-100, bg-zinc-900, bg-amber-300, bg-red-600, bg-emerald-500, bg-blue-700, bg-transparent |
| Texte | text-zinc-900, text-zinc-600, text-white, text-stone-50, font-serif, font-mono, font-light, font-bold, font-extrabold, uppercase, tracking-tight, tracking-widest, text-sm, text-lg |
| Espacement | px-3 py-1.5, px-5 py-2.5, px-8 py-4, gap-4 |
| Bordure | border, border-2, border-4, border-zinc-900, border-zinc-300, border-dashed, border-dotted, rounded-none, rounded-md, rounded-full |
| États | hover:bg-zinc-700, hover:text-white, hover:-translate-y-0.5, active:scale-95, focus-visible:outline-2 focus-visible:outline-offset-2, focus-visible:ring-2, transition |
| Ombre | shadow-xs, shadow-sm, shadow-lg, shadow-none, shadow-[4px_4px_0_0_#18181b] |
Critères observables
- Trois boutons, trois directions esthétiques nettement distinctes
- Chacun a un
hover:visible - Chacun a un focus clavier visible, testé au
[TAB] - Le texte contraste franchement avec son fond
- Aucun néon, aucun glassmorphism, aucun dégradé violet-rose
- Pas de classes inutiles empilées « au cas où »
Montre au prof cette checklist cochée et tes cinq boutons à l'écran.
Exercice 2 — Carte de profil
Durée indicative : 45 minutes.
C'est la future carte « à propos » de ton portfolio. Autant la faire te ressembler tout de suite.
Palier 2 — Complète les trous
Colle cette section. La structure est donnée, les décisions de style sont à toi.
<section class="mb-12">
<h2 class="text-2xl font-bold text-zinc-900 mb-4">Carte de profil</h2>
<!-- ??? ombre de la carte au repos : shadow-xs | shadow-sm | shadow-lg -->
<!-- ??? effet au survol de toute la carte : hover:shadow-xl | hover:-translate-y-1 | hover:border-zinc-900 -->
<article class="max-w-sm bg-white border border-zinc-200 overflow-hidden ??? ??? transition">
<!-- ??? hauteur de l'image : h-40 | h-48 | h-64 -->
<!-- ??? cadrage de l'image, pour qu'elle ne soit pas déformée : object-cover | object-contain | object-fill -->
<img
src="https://i.pravatar.cc/600"
alt="Portrait"
class="w-full ??? ???"
>
<!-- ??? padding intérieur du bloc texte : p-4 | p-6 | p-8 -->
<div class="???">
<!-- ??? taille et graisse du nom : text-xl font-bold | text-2xl font-bold | text-2xl font-serif -->
<h3 class="text-zinc-900 ???">Prénom Nom</h3>
<!-- ??? style du sous-titre, plus discret que le nom : text-sm text-zinc-500 | text-base text-zinc-600 | text-xs uppercase tracking-widest text-zinc-500 -->
<p class="mt-1 ???">Webdesign & développement</p>
<!-- ??? espace au-dessus de la bio : mt-2 | mt-4 | mt-6 -->
<p class="??? text-zinc-600 leading-relaxed">
Une ou deux phrases sur toi. Ce que tu fais, ce que tu cherches.
</p>
<!-- ??? bouton : reprends les classes d'un de tes boutons de l'exercice 1 -->
<button class="mt-6 ???">
Me contacter
</button>
</div>
</article>
</section>
Palier 3 — À toi
Consigne : refais cette carte dans ta direction — celle que tu as choisie à l'exercice 1. Même contenu, autre parti pris. Tu peux déplacer l'image, la supprimer, passer en deux colonnes, mettre le nom en énorme, ne garder qu'un trait.
Remplace le texte d'exemple par le tien : ton vrai prénom, ta vraie phrase. C'est la carte qui ira sur ton portfolio.
Banque de classes
| Catégorie | Classes |
|---|---|
| Fond | bg-white, bg-stone-100, bg-amber-100, bg-zinc-900 |
| Texte | font-serif, font-mono, text-sm, text-2xl, text-4xl, font-light, font-black, leading-relaxed, tracking-tight |
| Espacement | p-4, p-8, mt-2, mt-6, space-y-3, max-w-sm, max-w-md |
| Bordure | border, border-2, border-zinc-900, border-b, rounded-none, rounded-lg, overflow-hidden |
| Image | w-full, h-40, h-64, object-cover, aspect-square, rounded-full, grayscale |
| États | hover:shadow-xl, hover:-translate-y-1, transition, focus-visible:ring-2 |
| Responsive | md:flex, md:w-1/2, md:text-3xl, md:p-10 |
Critères observables
- Le contenu est le mien, pas le texte d'exemple
- La hiérarchie est claire : on lit le nom en premier, la bio en dernier
- L'image n'est pas déformée
- La carte réagit au survol
- Le bouton a un focus clavier visible
- La direction esthétique est la même qu'à l'exercice 1
Montre au prof la carte et la checklist.
Exercice 3 — Navbar responsive
Durée indicative : 30 minutes.
Palier 1 — Copie et observe
Cette navbar se place avant le <h1>, tout en haut du <main> — ou mieux, juste au-dessus, collée en haut de page.
<nav class="bg-white border-b-2 border-zinc-900 px-6 py-4 mb-10">
<div class="mx-auto max-w-5xl flex items-center justify-between">
<span class="text-xl font-bold tracking-tight text-zinc-900">MonSite</span>
<div class="hidden md:flex gap-8">
<a href="#" class="text-zinc-700 hover:text-zinc-900 transition">Accueil</a>
<a href="#" class="text-zinc-700 hover:text-zinc-900 transition">Projets</a>
<a href="#" class="text-zinc-700 hover:text-zinc-900 transition">Contact</a>
</div>
<button class="md:hidden text-2xl text-zinc-900" aria-label="Ouvrir le menu">
☰
</button>
</div>
</nav>

Trois manipulations :
- Ouvre les DevTools (
F12sur Windows,Cmd + Option + Isur Mac), active le mode responsive (l'icône téléphone/tablette, ouCtrl + Shift + M/Cmd + Option + M). Fais varier la largeur autour de 768 px : les liens disparaissent, le bouton hamburger apparaît. C'esthidden md:flexd'un côté etmd:hiddende l'autre. - Remplace
hidden md:flexparflex. Les liens restent visibles même en mobile, et ça déborde. Remets la version d'origine. - Remplace
md:parlg:partout (trois endroits). Le basculement se fait maintenant à 1024 px au lieu de 768.
Note
Le bouton hamburger ne fait rien pour l'instant : il n'y a pas de JavaScript. C'est normal, on le branchera quand on verra la réactivité de Vue.
Palier 2 — Complète les trous
Repars de la navbar ci-dessus et remplace ces classes par tes choix.
<!-- ??? fond de la navbar : bg-white | bg-stone-100 | bg-zinc-900 -->
<!-- ??? bordure basse : border-b border-zinc-200 | border-b-2 border-zinc-900 | border-b-4 border-amber-300 -->
<nav class="??? ??? px-6 py-4 mb-10">
<div class="mx-auto max-w-5xl flex items-center justify-between">
<!-- ??? taille du logo : text-lg | text-xl | text-2xl -->
<!-- ??? caractère du logo : font-bold tracking-tight | font-serif | font-mono uppercase tracking-widest -->
<span class="??? ??? text-zinc-900">MonSite</span>
<!-- ??? espacement entre les liens : gap-4 | gap-8 | gap-12 -->
<div class="hidden md:flex ???">
<!-- ??? survol des liens : hover:text-zinc-900 | hover:underline hover:underline-offset-4 | hover:bg-amber-300 -->
<a href="#" class="text-zinc-700 ??? transition">Accueil</a>
<a href="#" class="text-zinc-700 ??? transition">Projets</a>
<a href="#" class="text-zinc-700 ??? transition">Contact</a>
</div>
<button class="md:hidden text-2xl text-zinc-900" aria-label="Ouvrir le menu">
☰
</button>
</div>
</nav>
Critères observables
- En dessous de 768 px : liens cachés, hamburger visible
- Au-dessus : liens visibles, hamburger caché
- Les liens réagissent au survol
- Logo et liens lisibles sur le fond choisi
- La navbar ne déborde pas horizontalement en mobile
Montre au prof le basculement en direct dans les DevTools.
Exercice 4 — Formulaire de contact
Durée indicative : 40 minutes.
Palier 2 — Complète les trous
La structure HTML est complète, y compris les for / id qui associent chaque label à son champ. Tu ne touches qu'aux classes.
<section class="mb-12">
<h2 class="text-2xl font-bold text-zinc-900 mb-4">Me contacter</h2>
<!-- ??? fond et cadre du bloc : bg-white border border-zinc-200 | bg-white border-2 border-zinc-900 | bg-stone-50 border-none -->
<!-- ??? padding du bloc : p-6 | p-8 | p-10 -->
<form class="max-w-md ??? ??? space-y-5">
<div>
<!-- ??? style des labels : text-sm font-medium text-zinc-700 | text-xs uppercase tracking-widest text-zinc-500 | text-base font-bold text-zinc-900 -->
<label for="nom" class="block mb-1.5 ???">Nom complet</label>
<!-- ??? bordure du champ au repos : border border-zinc-300 | border-2 border-zinc-900 | border-b-2 border-zinc-400 -->
<!-- ??? focus clavier visible : focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-900 | focus-visible:ring-2 focus-visible:ring-zinc-900 | focus-visible:border-zinc-900 focus-visible:bg-amber-50 -->
<input
id="nom"
type="text"
placeholder="Prénom Nom"
class="w-full px-4 py-2.5 rounded-md placeholder:text-zinc-400 ??? ???"
>
</div>
<div>
<label for="email" class="block mb-1.5 ???">Email</label>
<input
id="email"
type="email"
placeholder="prenom@exemple.be"
class="w-full px-4 py-2.5 rounded-md placeholder:text-zinc-400 ??? ???"
>
</div>
<div>
<label for="message" class="block mb-1.5 ???">Message</label>
<!-- ??? mêmes bordure et focus que les champs ci-dessus, pour rester cohérent -->
<textarea
id="message"
rows="4"
placeholder="Ton message"
class="w-full px-4 py-2.5 rounded-md placeholder:text-zinc-400 ??? ???"
></textarea>
</div>
<!-- ??? bouton d'envoi, pleine largeur : w-full bg-zinc-900 text-stone-50 | w-full bg-red-600 text-white | w-full bg-amber-300 text-zinc-900 border-2 border-zinc-900 -->
<!-- ??? survol du bouton : hover:bg-zinc-700 | hover:opacity-90 | hover:-translate-y-0.5 -->
<button
type="submit"
class="??? px-6 py-3 rounded-md font-medium ??? focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-900 transition"
>
Envoyer
</button>
</form>
</section>
Checklist accessibilité
Celle-ci, on la vérifie ensemble, champ par champ.
- Chaque champ a un
<label>avec unforqui correspond exactement à sonid - Je clique sur le texte du label : le curseur saute dans le champ (c'est le test qui prouve que l'association marche)
- Je parcours tout le formulaire au
[TAB]: à chaque étape, je vois où je suis - Le focus est visible sur les trois champs et sur le bouton
- Le texte des labels contraste franchement avec le fond (pas de
text-zinc-400sur blanc) - Le
placeholderne remplace pas le label : il donne un exemple, il ne dit pas ce qu'on demande - Les trois champs ont le même traitement visuel
Montre au prof le parcours complet au clavier, sans toucher la souris.
Exercice 5 — Grille de projets
Durée indicative : 45 minutes.
C'est la future grille de projets de ton portfolio.
Palier 1 — Copie et observe
Voici une carte. Tu la colles dans la grille, puis tu la dupliques cinq fois de plus (copier-coller) pour en avoir six, en changeant le ?random=1 en 2, 3… et le titre de chaque projet.
<section class="mb-12">
<h2 class="text-2xl font-bold text-zinc-900 mb-4">Mes projets</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<article class="bg-white border border-zinc-200 overflow-hidden hover:shadow-lg transition">
<img
src="https://picsum.photos/600/400?random=1"
alt="Aperçu du projet 1"
class="w-full h-48 object-cover"
>
<div class="p-5">
<h3 class="text-lg font-bold text-zinc-900">Projet 1</h3>
<p class="mt-2 text-sm text-zinc-600 leading-relaxed">
Une phrase qui dit ce que c'est et pour qui.
</p>
<span class="mt-4 inline-block text-xs uppercase tracking-widest text-zinc-500">
Design
</span>
</div>
</article>
<!-- Duplique cet <article> cinq fois de plus -->
</div>
</section>

Deux manipulations :
- Ouvre les DevTools en mode responsive et fais varier la largeur : 1 colonne en mobile, 2 dès 768 px (
md:grid-cols-2), 3 dès 1024 px (lg:grid-cols-3). Changelg:grid-cols-3enlg:grid-cols-4et observe. - Supprime
object-coversur une seule carte et compare-la à ses voisines : l'image est écrasée. C'est à ça que sert cette classe.
Palier 3 — À toi
Consigne : redessine cette carte dans la direction que tu as choisie à l'exercice 1. Une seule carte à concevoir, puis tu répercutes sur les six (copier-coller). Tu peux tout changer : supprimer l'image, la mettre en noir et blanc, passer le titre en énorme, mettre le tag en tampon coloré, retirer les bordures, inverser les couleurs.
Remplace les faux titres par tes vrais projets — même s'ils ne sont pas encore faits : les projets de l'année, un travail de l'an dernier, une envie. Tu réécriras plus tard, mais du texte réel donne un vrai design.
Banque de classes
| Catégorie | Classes |
|---|---|
| Fond | bg-white, bg-stone-100, bg-zinc-900, bg-amber-100, bg-blue-700 |
| Texte | text-lg, text-2xl, font-serif, font-mono, font-black, uppercase, tracking-widest, text-zinc-600, text-stone-50, line-clamp-2 |
| Espacement | p-4, p-6, gap-2, gap-6, gap-10, mt-2, mt-4 |
| Bordure | border, border-2, border-zinc-900, border-t-4, rounded-none, rounded-lg, overflow-hidden |
| Image | h-40, h-56, object-cover, aspect-video, aspect-square, grayscale, hover:grayscale-0 |
| États | hover:shadow-lg, hover:-translate-y-1, hover:bg-amber-300, transition, focus-visible:ring-2 |
| Responsive | grid-cols-1, sm:grid-cols-2, md:grid-cols-2, lg:grid-cols-3, md:gap-8 |
Critères observables
- Six cartes, toutes traitées exactement pareil
- La grille passe de 1 à 2 à 3 colonnes selon la largeur
- Les images ne sont pas déformées
- Les titres sont mes vrais projets, pas « Projet 1 »
- La carte réagit au survol
- La direction esthétique est la même que celle des exercices 1 et 2
- Aucun néon, aucun glassmorphism, aucun dégradé violet-rose
Montre au prof la grille en mode responsive et la checklist.
Chercher par soi-même
Dans la doc Tailwind
- Va sur tailwindcss.com/docs.
- Utilise la barre de recherche (
Ctrl + Ksur Windows,Cmd + Ksur Mac), en anglais et par propriété CSS, pas par intention : chercheborder-radiusplutôt que « coins arrondis »,object-fitplutôt que « image pas déformée ». - Chaque page liste toutes les classes de la famille avec leur équivalent CSS. Quand tu as le CSS en tête, tu as la classe.
Attention
Une recherche Google te renverra souvent vers des articles écrits pour Tailwind v3. Les signes qui trahissent : un tailwind.config.js, bg-gradient-to-r (v4 : bg-linear-to-r), flex-shrink-0 (v4 : shrink-0). En cas de doute, la doc officielle fait foi.
Demander à une IA sans se faire refiler du slop
Une IA à qui tu ne donnes pas de direction te rendra la moyenne d'Internet : dégradé violet-rose, coins arrondis, ombre floue, glassmorphism. Ce n'est pas la faute du modèle, c'est la tienne : tu n'as rien décidé, donc elle décide à ta place, et elle décide comme tout le monde.
Mauvais prompt
Fais-moi un bouton stylé en Tailwind.
Bon prompt
Écris le template d'un bouton en Tailwind CSS v4, classes uniquement, pas de CSS custom. Direction : imprimé riso — un aplat de couleur franche, une bordure noire de 2 px, angles droits, aucune ombre floue. Palette : fond crème
stone-100, encrezinc-900, accentamber-300. Contraintes : focus clavier visible avecfocus-visible:, contraste texte/fond d'au moins 4,5:1, maximum 10 classes. Interdits : dégradés,backdrop-blur, néon,shadow-xl. Donne-moi trois variantes qui diffèrent vraiment, pas trois nuances de la même.
La différence tient en quatre choses : une direction nommée, une palette imposée, des contraintes vérifiables, des interdits explicites. Et surtout : tu dois pouvoir dire pourquoi tu gardes le résultat. Si tu ne peux pas, rebriefe.
Astuce
Demande toujours de courtes réponses et relis chaque classe. Une IA invente des classes qui n'existent pas (c'est arrivé à ton prof avec shadow-brutalist). L'autocomplétion de VS Code est ton filtre : une classe qui n'apparaît pas dans la liste n'existe pas.
Récap
- Palier 1 : on copie, on modifie une classe à la fois, on regarde.
- Palier 2 : on choisit parmi des candidates — plusieurs réponses sont justes, c'est l'intention qui tranche.
- Palier 3 : on conçoit, avec des critères observables et une banque de classes.
- Une direction esthétique tenue sur les exercices 1, 2 et 5 : c'est déjà l'identité de ton portfolio.
- Focus visible et contraste sur chaque exercice. Ce n'est pas du bonus.
- Cherche par propriété CSS dans la doc v4 ; brief une IA avec direction, palette, contraintes et interdits.



