Style avec des classes utilitaires
Création de composantes complexes à partir d'un ensemble limité d'utilitaires primitifs.
Aperçu
Avec Tailwind, vous stylisez vos éléments en combinant directement dans votre balisage de nombreuses classes de présentation à usage unique (classes utilitaires) :
- <div class="mx-auto flex max-w-sm items-center gap-x-4 rounded-xl bg-white p-6 shadow-lg outline outline-black/5 dark:bg-slate-800 dark:shadow-none dark:-outline-offset-1 dark:outline-white/10">
- <img class="size-12 shrink-0" src="/img/logo.svg" alt="ChitChat Logo" />
- <div>
- <div class="text-xl font-medium text-black dark:text-white">ChitChat</div>
- <p class="text-gray-500 dark:text-gray-400">Vous avez un nouveau message !</p>
- </div>
- </div>
Par exemple, dans l'interface utilisateur ci-dessus, nous avons utilisé :
- Les utilitaires d'affichage (display) et (padding) de marge intérieure (flex, shrink-0 et p-6) pour contrôler la mise en page générale.
- Les utilitaires de largeur maximale (max-width) et (margin) de marge (max-w-sm et mx-auto) pour limiter la largeur de la carte et la centrer horizontalement.
- Les utilitaires de couleur d'arrière-plan (background-color), de rayon de bordure (border-radius) et (box-shadow) d'ombre portée (bg-white, rounded-xl et shadow-lg) pour styliser l'apparence de la carte.
- Les utilitaires de largeur (width) et (height) de hauteur (size-12) pour définir la largeur et la hauteur de l'image du logo.
- Le gap, l'utilitaire d'espacement (gap-x-4) pour gérer l'espacement entre le logo et le texte.
- Les utilitaires de taille (font-size), de couleur (color) et (font-weight) d'épaisseur de police de caractères (text-xl, text-black, font-medium,...) pour styliser le texte de la carte.
Cette approche de style va à l'encontre de nombreuses bonnes pratiques traditionnelles, mais une fois que vous l'aurez essayée, vous constaterez rapidement des avantages considérables :
- Vous gagnez en rapidité : vous ne perdez plus de temps à trouver des noms de classes, à choisir des sélecteurs ou à jongler entre fichiers HTML et CSS. Vos maquettes prennent forme très rapidement.
- Les modifications sont plus sûres : ajouter ou supprimer une classe utilitaire à un élément n'affecte que cet élément. Vous n'avez donc pas à craindre de casser accidentellement quelque chose sur une autre page utilisant le même CSS.
- La maintenance des anciens projets est simplifiée : modifier un élément consiste simplement à le trouver dans votre projet et à modifier ses classes, sans avoir à vous souvenir du fonctionnement de tout ce CSS personnalisé que vous n'avez pas utilisé depuis six mois.
- Votre code est plus portable : puisque la structure et le style sont centralisés, vous pouvez facilement copier-coller des portions entières d'interface utilisateur, même entre différents projets.
- Votre CSS n'augmente plus de façon linéaire : grâce à la grande réutilisabilité des classes utilitaires, votre CSS ne croît plus de façon exponentielle à chaque nouvelle fonctionnalité ajoutée à un projet.
Ces avantages font une grande différence sur les petits projets, mais ils sont encore plus précieux pour les équipes travaillant sur des projets de longue durée et à grande échelle.
Pourquoi ne pas simplement utiliser des styles en ligne ?
On entend souvent dire : «N'est-ce pas tout simplement des styles en ligne ?» Et d'une certaine manière, oui : on applique des styles directement aux éléments au lieu de leur attribuer un nom de classe puis de styliser cette classe.
Cependant, l'utilisation de classes utilitaires présente de nombreux avantages importants par rapport aux styles en ligne, par exemple :
- Conception avec des contraintes : avec les styles en ligne, chaque valeur est un nombre magique. Avec les classes utilitaires, on choisit des styles parmi un système de conception prédéfini, ce qui facilite grandement la création d'interfaces utilisateur visuellement cohérentes.
- États de survol, de focus et autres : les styles en ligne ne peuvent pas cibler des états comme le survol ou le focus, mais les variantes d'état de Tailwind permettent de styliser facilement ces états avec des classes utilitaires.
- Requêtes média : on ne peut pas utiliser de requêtes média dans les styles en ligne, mais on peut utiliser les variantes responsives de Tailwind pour créer facilement des interfaces entièrement responsives.
Cette composante est entièrement adaptatif et comprend un bouton avec des styles de survol et d'activation, et est entièrement construit avec des classes utilitaires :
- <div class="flex flex-col gap-2 p-8 sm:flex-row sm:items-center sm:gap-6 sm:py-4 ...">
- <img class="mx-auto block h-24 rounded-full sm:mx-0 sm:shrink-0" src="/img/steve-tremblay.jpg" alt="" />
- <div class="space-y-2 text-center sm:text-left">
- <div class="space-y-0.5">
- <p class="text-lg font-semibold text-black">Steve Tremblay</p>
- <p class="font-medium text-gray-500">Ingénieur produit</p>
- </div>
- <button class="border-purple-200 text-purple-600 hover:border-transparent hover:bg-purple-600 hover:text-white active:bg-purple-700 ...">
- Message
- </button>
- </div>
- </div>
Penser en classes utilitaires
Style des états de survol et de focus
Pour styliser un élément lors de l'obtention d'un état comme le survol ou le focus, préfixez la classe utilitaire avec l'état ciblé, par exemple 'hover:bg-sky-700' :
- <button class="bg-sky-500 hover:bg-sky-700 ...">Enregistrer les modifications</button>
Ces préfixes sont appelés variantes dans Tailwind, et ils n'appliquent les styles d'une classe utilitaire que lorsque la condition de cette variante est remplie.
Voici à quoi ressemble le CSS généré pour la classe hover:bg-sky-700 :
- .hover\:bg-sky-700 {
- &:hover {
- background-color: var(--color-sky-700);
- }
- }
Remarquez que cette classe reste inactive tant que l'élément n'est pas survolé. Son seul rôle est de définir les styles au survol, rien de plus.
C'est différent du CSS traditionnel, où une seule classe gère généralement les styles pour plusieurs états :
- <button class="btn">Enregistrer les modifications</button>
- <style>
- .btn {
- background-color: var(--color-sky-500);
- &:hover {
- background-color: var(--color-sky-700);
- }
- }
- </style>
Vous pouvez même empiler des variantes dans Tailwind pour appliquer un utilitaire lorsque plusieurs conditions correspondent, comme combiner hover: et disabled:
- <button class="bg-sky-500 disabled:hover:bg-sky-500 ...">Enregistrer les modifications</button>
Requêtes média et points de rupture
Tout comme pour les états de survol et de focus, vous pouvez styliser des éléments à différents points de rupture en préfixant n'importe quelle fonction utilitaire avec le point de rupture auquel vous souhaitez appliquer ce style :
Dans l'exemple ci-dessus, le préfixe sm: garantit que grid-cols-3 ne se déclenche qu'à partir du point de rupture sm, étant de 40rem par défaut :
- .sm\:grid-cols-3 {
- @media (width >= 40rem) {
- grid-template-columns: repeat(3, minmax(0, 1fr));
- }
- }
Ciblage du mode sombre
Pour styliser un élément en mode sombre, il suffit d'ajouter le préfixe `dark:` à l'utilitaire que vous souhaitez appliquer lorsque le mode sombre est actif :
- <div class="bg-white dark:bg-gray-800 rounded-lg px-6 py-8 ring shadow-xl ring-gray-900/5">
- <div>
- <span class="inline-flex items-center justify-center rounded-md bg-indigo-500 p-2 shadow-lg">
- <svg
- class="h-6 w-6 text-white"
- fill="none"
- viewBox="0 0 24 24"
- stroke="currentColor"
- aria-hidden="true"
- >
- <!-- ... -->
- </svg>
- </span>
- </div>
- <h3 class="text-gray-900 dark:text-white mt-5 text-base font-medium tracking-tight ">Écrit à l'envers</h3>
- <p class="text-gray-500 dark:text-gray-400 mt-2 text-sm ">
- Le stylo Zero Gravity permet d'écrire dans n'importe quelle orientation, même à l'envers. Il fonctionne même dans l'espace.
- </p>
- </div>
Tout comme pour les états de survol ou les requêtes média, il est important de comprendre qu'une seule classe utilitaire n'inclura jamais à la fois les styles clair et sombre ; vous stylisez les éléments en mode sombre en utilisant plusieurs classes, une pour les styles du mode clair et une autre pour les styles du mode sombre :
- .dark\:bg-gray-800 {
- @media (prefers-color-scheme: dark) {
- background-color: var(--color-gray-800);
- }
- }
Utilisation de la composition de classes
Avec Tailwind, il est fréquent d'utiliser plusieurs classes pour définir la valeur d'une seule propriété CSS, par exemple pour ajouter plusieurs filtres à un élément :
Ces deux effets reposent sur la propriété `filter` en CSS; Tailwind utilise donc des variables CSS pour permettre de les combiner :
Le CSS généré ci-dessus est légèrement simplifié, mais l'astuce consiste à définir une variable CSS pour chaque effet spécifique. La propriété `filter` examine ensuite toutes ces variables et ne renvoie aucune valeur si la variable n'est pas définie.
Tailwind utilise cette même approche pour les dégradés, les couleurs d'ombre, les transformations,...
Utilisation de valeurs arbitraires
De nombreuses utilitaires de Tailwind sont pilotés par des variables de thème, telles que bg-blue-500, text-xl et shadow-md, correspondant à votre palette de couleurs, à l'échelle de votre texte et aux ombres.
Lorsque vous devez utiliser une valeur ponctuelle en dehors de votre thème, utilisez la syntaxe spéciale entre crochets pour spécifier des valeurs arbitraires :
- <button class="bg-[#316ff6] ...">
- Se connecter avec Facebook
- </button>
Cela peut s'avérer utile pour des couleurs ponctuelles hors de votre palette (comme le bleu Facebook ci-dessus), mais aussi lorsque vous avez besoin d'une valeur personnalisée complexe, comme une grille très spécifique :
C'est également utile lorsque vous devez utiliser des fonctionnalités CSS comme calc(), même si vous utilisez les valeurs de votre thème :
Il existe même une syntaxe permettant de générer du CSS totalement arbitraire, y compris un nom de propriété arbitraire, ce qui peut être utile pour définir des variables CSS :
Comment ça marche ?
Tailwind CSS n'est pas une simple feuille de style statique comme celles utilisées par d'autres cadres d'application CSS. Il génère le CSS nécessaire en fonction des classes que vous utilisez lors de la compilation.
Pour ce faire, il analyse tous les fichiers de votre projet à la recherche de symboles pouvant correspondre à un nom de classe.
Une fois toutes les classes potentielles identifiées, Tailwind génère le CSS correspondant et le compile en une seule feuille de style ne contenant que les styles nécessaires.
Comme le CSS est généré à partir du nom de la classe, Tailwind peut reconnaître les classes utilisant des valeurs arbitraires telles que `bg-[#316ff6]` et générer le CSS nécessaire, même si la valeur ne fait pas partie de votre thème.
Sélecteurs complexes
Il est parfois nécessaire de styliser un élément en fonction de plusieurs conditions, par exemple en mode sombre, à un point de rupture spécifique, au survol de la souris et lorsque l'élément possède un attribut de données particulier.
Voici un exemple avec Tailwind :
- <button class="dark:lg:data-current:hover:bg-indigo-600 ...">
- <!-- ... -->
- </button>
- @media (prefers-color-scheme: dark) and (width >= 64rem) {
- button[data-current]:hover {
- background-color: var(--color-indigo-600);
- }
- }
Tailwind prend également en charge des fonctionnalités comme group-hover, vous permettant de styliser un élément lorsqu'un parent spécifique est survolé :
- @media (hover: hover) {
- a:hover span {
- text-decoration-line: underline;
- }
- }
Cette syntaxe `group-*` fonctionne également avec d'autres variantes, comme `group-focus`, `group-active`, et bien d'autres.
Pour les scénarios vraiment complexes (notamment lors de la mise en forme de HTML que vous ne contrôlez pas), Tailwind prend en charge des variantes arbitraires qui vous permettent d'écrire n'importe quel sélecteur directement dans un nom de classe :
Quand utiliser les styles en ligne
Les styles en ligne restent très utiles dans les projets Tailwind CSS, notamment lorsqu'une valeur provient d'une source dynamique comme une base de données ou une API :
Vous pouvez également opter pour un style en ligne pour les valeurs arbitraires très complexes qui sont difficiles à lire lorsqu'elles sont formatées comme un nom de classe :
Un autre modèle utile consiste à définir des variables CSS à partir de sources dynamiques à l'aide de styles en ligne, puis à référencer ces variables avec des classes utilitaires :
- export function BrandedButton({ buttonColor, buttonColorHover, textColor, children }) {
- return (
- <button
- style={{
- "--bg-color": buttonColor,
- "--bg-color-hover": buttonColorHover,
- "--text-color": textColor,
- }}
- className="bg-(--bg-color) text-(--text-color) hover:bg-(--bg-color-hover) ..."
- >
- {children}
- </button>
- );
- }
Gestion des duplications
Lorsque vous développez des projets entiers à l'aide de classes utilitaires, vous serez inévitablement amené à répéter certains modèles pour recréer le même design à différents endroits.
Par exemple, ici, les classes utilitaires de chaque image d'avatar sont répétées cinq fois :
- <div>
- <div class="flex items-center space-x-2 text-base">
- <h4 class="font-semibold text-slate-900">Contributeurs</h4>
- <span class="bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-700 ...">204</span>
- </div>
- <div class="mt-3 flex -space-x-2 overflow-hidden">
- <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="photo-1" alt="" />
- <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="photo-2" alt="" />
- <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="photo-3" alt="" />
- <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="photo-4" alt="" />
- <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src="photo-5" alt="" />
- </div>
- <div class="mt-3 text-sm font-medium">
- <a href="#" class="text-blue-500">+ 198 autres personnes</a>
- </div>
- </div>
Pas de panique ! En pratique, ce n'est pas le problème que vous craignez, et les stratégies pour y faire face sont des choses que vous faites déjà tous les jours.
Utilisation des boucles
Bien souvent, un élément graphique apparaissant plusieurs fois sur la page rendue n'est en réalité créé qu'une seule fois, car le balisage est généré dans une boucle.
Par exemple, les avatars dupliqués au début de ce guide seraient presque certainement générés dans une boucle dans un projet réel :
- <div>
- <div class="flex items-center space-x-2 text-base">
- <h4 class="font-semibold text-slate-900">Contributeurs</h4>
- <span class="bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-700 ...">204</span>
- </div>
- <div class="mt-3 flex -space-x-2 overflow-hidden">
- {#each contributors as user}
- <img class="inline-block h-12 w-12 rounded-full ring-2 ring-white" src={user.avatarUrl} alt={user.handle} />
- {/each}
- </div>
- <div class="mt-3 text-sm font-medium">
- <a href="#" class="text-blue-500">+ 198 autres personnes</a>
- </div>
- </div>
Lorsque les éléments sont rendus en boucle comme ceci, la liste des classes n'est écrite qu'une seule fois, il n'y a donc pas de problème de duplication à résoudre.
Utilisation de l'édition multi-curseurs
Lorsque la duplication est localisée dans un groupe d'éléments d'un même fichier, la méthode la plus simple consiste à utiliser l'édition multi-curseurs pour sélectionner et modifier rapidement la liste des classes de chaque élément simultanément :
- <nav class="flex justify-center space-x-4">
- <a href="/dashboard" class="font-medium rounded-lg px-3 py-2 text-gray-700 hover:bg-gray-100 hover:text-gray-900">
- Accueil
- </a>
- <a href="/team" class="font-medium rounded-lg px-3 py-2 text-gray-700 hover:bg-gray-100 hover:text-gray-900">
- Équipe
- </a>
- <a href="/projects" class="font-medium rounded-lg px-3 py-2 text-gray-700 hover:bg-gray-100 hover:text-gray-900">
- Projets
- </a>
- <a href="/reports" class="font-medium rounded-lg px-3 py-2 text-gray-700 hover:bg-gray-100 hover:text-gray-900">
- Rapports
- </a>
- </nav>
Vous seriez surpris de voir à quel point cette solution s'avère souvent la meilleure. Si vous pouvez modifier rapidement et simultanément toutes les listes de classes dupliquées, il est inutile d'introduire une abstraction supplémentaire.
Utilisation des composantes
Si vous devez réutiliser certains styles dans plusieurs fichiers, la meilleure stratégie consiste à créer une composante si vous utilisez un cadre d'application front-end comme React, Svelte ou Vue, ou un modèle partiel si vous utilisez un langage de gabarit comme Blade, ERB, Twig ou Nunjucks.
- export function VacationCard({ img, imgAlt, eyebrow, title, pricing, url }) {
- return (
- <div>
- <img className="rounded-lg" src={img} alt={imgAlt} />
- <div className="mt-4">
- <div className="text-xs font-bold text-sky-500">{eyebrow}</div>
- <div className="mt-1 font-bold text-gray-700">
- <a href={url} className="hover:underline">
- {title}
- </a>
- </div>
- <div className="mt-2 text-sm text-gray-600">{pricing}</div>
- </div>
- </div>
- );
- }
Vous pouvez désormais utiliser ce composant à autant d'endroits que vous le souhaitez, tout en conservant une source unique de référence pour les styles, ce qui permet de les mettre à jour facilement et simultanément en un seul endroit.
Utilisation de CSS personnalisé
Si vous utilisez un langage de gabarit comme ERB ou Twig plutôt que React ou Vue, créer un modèle partiel pour un simple bouton peut sembler disproportionné par rapport à une classe CSS simple comme `btn`.
Bien qu'il soit fortement recommandé de créer des modèles partiels adaptés aux composants plus complexes, l'utilisation de CSS personnalisé est tout à fait acceptable lorsqu'un modèle partiel paraît trop lourd.
Voici à quoi pourrait ressembler une classe `btn-primary`, utilisant des variables de thème pour garantir la cohérence du design :
- <button class="btn-primary">Enregistrer les modifications</button>
- @import "tailwindcss";
- @layer components {
- .btn-primary {
- border-radius: calc(infinity * 1px);
- background-color: var(--color-violet-500);
- padding-inline: --spacing(5);
- padding-block: --spacing(2);
- font-weight: var(--font-weight-semibold);
- color: var(--color-white);
- box-shadow: var(--shadow-md);
- &:hover {
- @media (hover: hover) {
- background-color: var(--color-violet-700);
- }
- }
- }
- }
Cependant, pour tout ce qui est plus complexe qu'un simple élément HTML, nous recommandons fortement l'utilisation de modèles partiels afin que les styles et la structure puissent être encapsulés en un seul endroit.
Gestion des conflits de styles
Classes utilitaires conflictuelles
Lorsque deux classes ciblent la même propriété CSS, c'est la dernière classe définie dans la feuille de style qui prévaut. Dans cet exemple, l'élément recevra donc `display: grid` même si `flex` apparaît en dernier dans l'attribut de classe :
En règle générale, il ne faut jamais ajouter deux classes conflictuelles au même élément ; il faut n'ajouter que celle que vous souhaitez réellement appliquer :
Avec des bibliothèques basées sur des composants comme React ou Vue, cela signifie souvent exposer des propriétés spécifiques pour les personnalisations de style au lieu de laisser les utilisateurs ajouter des classes supplémentaires depuis l'extérieur d'un composant, car ces styles entrent souvent en conflit.
Utilisation du modificateur important
Lorsque vous devez absolument forcer l'application d'une classe utilitaire spécifique et que vous n'avez aucun autre moyen de gérer cette spécificité, vous pouvez ajouter ! à la fin du nom de la classe pour rendre toutes les déclarations !important :
- .bg-red-500\! {
- background-color: var(--color-red-500) !important;
- }
- .bg-teal-500 {
- background-color: var(--color-teal-500);
- }
Utilisation de l'indicateur « !important »
Si vous ajoutez Tailwind à un projet contenant déjà du CSS complexe avec des règles très spécifiques, vous pouvez utiliser l'indicateur «!important» lors de l'importation de Tailwind pour marquer tous les utilitaires comme « !important ».
- @import "tailwindcss" important;
- @layer utilities {
- .flex {
- display: flex !important;
- }
- .gap-4 {
- gap: 1rem !important;
- }
- .underline {
- text-decoration-line: underline !important;
- }
- }
Utilisation de l'option de préfixe
Si votre projet contient des noms de classes entrant en conflit avec les utilitaires CSS de Tailwind, vous pouvez préfixer toutes les classes et variables CSS générées par Tailwind à l'aide de l'option de préfixe :
- @import "tailwindcss" prefix(tw);