Tailwind
Tailwindcss
Senior
Tailwind-V4
Tailwind-Plugins
Design-Tokens
Shadcn
Cva
Oklch
Container-Queries
Rtl-Tailwind
Accessibilite-Tailwind
Tailwind-Architecture
Entretien-Tailwind-Senior
📝
Entretien Tailwind CSS Senior — Architecture, Plugins, v4
20 questions Tailwind niveau senior : Tailwind v4, OKLCH, plugins custom, design tokens, RTL, accessibilité, container queries, shadcn/ui, performance, migration legacy.
Banque de révision : les 60 questions corrigées
Voici l'intégralité des 60 questions de « Entretien Tailwind CSS Senior — Architecture, Plugins, v4 », chacune avec sa bonne réponse et son explication. Le quiz interactif ci-dessus en tire une sélection aléatoire à chaque tentative — utilisez cette liste complète pour réviser.
Explication :
Tailwind v4 (2024–2025) introduit : (1) config CSS-first dans le fichier CSS via
@theme { --color-brand: ...; }, @import "tailwindcss" remplace les 3 directives ; (2) moteur Oxide (Rust + LightningCSS) — build 5–10× plus rapide ; (3) espace OKLCH pour les couleurs (plus perceptuellement uniforme) ; (4) support natif des Cascade Layers, container queries, :has(), color-mix().
En entretien : tailwind.config.js reste utilisable en v4 via
@config "./tailwind.config.js", donc migration progressive possible.
Explication :
L'API plugin officielle : const plugin = require('tailwindcss/plugin');
module.exports = {
plugins: [
plugin(({ addUtilities }) => {
addUtilities({
'.scrollbar-thin': { 'scrollbar-width': 'thin' },
'.scrollbar-none': { 'scrollbar-width': 'none' }
});
})
]
}Les méthodes utiles :
addUtilities, addComponents, addBase, matchUtilities (avec valeurs dynamiques), addVariant.
Avec
matchUtilities, tu peux faire scrollbar-{couleur} qui accepte n'importe quelle couleur du thème, comme un utility natif.
Explication :
ms-4 (margin-inline-start) = margin-left: 1rem en LTR mais margin-right: 1rem en RTL automatiquement. Idem me-*, ps-*, pe-*, start-*, end-*, border-s-*, border-e-*, text-start, text-end. Indispensable pour les apps i18n (arabe, hébreu, persan).
Pattern : utilise
ps/pe/ms/me par défaut au lieu de pl/pr/ml/mr pour rendre toute la codebase RTL-ready dès le début.
Explication :
@config permet de continuer à utiliser un tailwind.config.js classique en v4, sans devoir tout migrer vers la config CSS-first. Pratique pour les migrations progressives.
Explication :
Pattern moderne : :root { --color-primary: 220 90% 50%; }
[data-theme="acme"] { --color-primary: 0 80% 60%; }
// config
colors: { primary: 'hsl(var(--color-primary) / <alpha-value>)' }Une seule build Tailwind, switch de thème par classe ou attribut. C'est la base de shadcn/ui, Radix, Vercel Design System.
Tailwind v4 supporte nativement
@theme inline et CSS vars en first-class — le pattern devient trivial.
Explication :
matchUtilities mappe un utility sur un set de valeurs : matchUtilities({
'text-shadow': (value) => ({ textShadow: value })
}, { values: theme('textShadow') });Génère text-shadow-sm, text-shadow-md... + accepte la syntaxe arbitraire text-shadow-[2px_2px_red].
Explication :
addVariant('third', '&:nth-child(3)') dans un plugin, et third:bg-red-500 devient utilisable. matchVariant permet aussi des variants paramétrés : matchVariant('nth', (value) => `&:nth-child(${value})`) → utilisable comme nth-3:bg-red.
Utile en pratique pour
data-[state=open]:, aria-[disabled]:, group-data-[state=open]:... — Headless UI/Radix s'en servent intensivement.
Explication :
Tailwind v3.2 introduit les attribute selectors arbitraires :
data-[state=open]:bg-blue-500, aria-[expanded=true]:rotate-180, data-[orientation=vertical]:flex-col. Indispensable pour styler les composants Radix/Headless UI.
Explication :
Tailwind brille pour les apps avec design system cohérent et un grand nombre de composants. CSS Modules reste pertinent quand : (1) animations CSS complexes (keyframes longs, multistep), (2) styles très spécifiques peu réutilisables, (3) équipe historique CSS-first, (4) contraintes de bundle ultra-serrées (rares). Les deux peuvent cohabiter dans un même projet sans conflit.
Bonne pratique : Tailwind pour 90% du UI, CSS classique pour les exceptions complexes (graphiques, transitions multi-étapes, print styles).
Explication :
has-[selector]: compile en :has(...). Permet de styler un parent en fonction d'un descendant. Exemple : has-[input:checked]:bg-blue-100 = applique un fond bleu si l'élément contient un input coché. Compatible navigateurs modernes (depuis 2023).
Explication :
shadcn/ui n'est pas une lib, c'est un générateur :
npx shadcn add button copie le composant dans ton repo, basé sur Radix UI (logique a11y) + Tailwind (style) + cn() helper (clsx + tailwind-merge). Tu modifies librement, pas de version coincée par une dépendance externe. Devenu standard de fait sur les apps Next.js modernes.
Comprendre le pattern
cva (class-variance-authority) que shadcn utilise : déclaration des variants en TypeScript avec types autocomplétés.
Explication :
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs) { return twMerge(clsx(inputs)); }
clsx assemble conditionnellement, twMerge résout les conflits Tailwind (ex: p-2 + p-4 → garde p-4). Le helper cn() est devenu un de facto standard de l'écosystème.
Explication :
À grande échelle, le CSS final reste petit (souvent < 30 Ko gzipped) si la config est saine. Pièges principaux : safelist regex trop permissive qui regénère des milliers de classes ;
theme sans extend qui force la régénération ; classes construites dynamiquement ('bg-' + color) invisibles au scanner. Le moteur Oxide (v4) accélère le build de 5–10×.
Outil de debug :
npx tailwindcss --content './src/**/*' -o out.css --minify et inspecter out.css pour voir ce qui est généré.
Explication :
scheme-dark et scheme-light appliquent color-scheme, qui affecte les UI natives du navigateur (scrollbars, form controls, autofill). À combiner avec dark mode quand on veut une cohérence totale.
Explication :
supports-[property:value]: compile en @supports (property: value) { ... }. Exemple : supports-[backdrop-filter]:backdrop-blur-md n'applique le flou que si le navigateur le supporte. Excellent pattern de progressive enhancement.
Explication :
Pattern monorepo :
presets est un tableau de configs Tailwind héritées. Chaque app peut avoir son propre tailwind.config.js qui étend le preset commun. Permet de partager design tokens (couleurs, fonts, spacings) tout en autorisant overrides locaux. Idéal pour Nx, Turborepo, Lerna.
Les presets s'empilent : preset A → preset B → config app — chaque niveau peut override le précédent.
Explication :
Depuis v3.4, les not-* variants sont natifs :
not-first: = :not(:first-child), not-last:, not-disabled:, not-checked:... Pratique pour styler tous sauf un. Exemple : not-last:border-b = bordure sauf sur le dernier.
Explication :
Storybook documente chaque variant de composant. Chromatic (de Storybook) ou Percy/Loki capturent des screenshots et détectent les régressions visuelles sur chaque PR. Sur un projet Tailwind avec design system, c'est le filet de sécurité indispensable contre les régressions de design tokens (changer une couleur peut toucher 200 composants).
Combo classique : Storybook + Chromatic + Playwright pour les tests d'intégration → couvrent visuel, comportement, accessibilité.
Explication :
view-transition-name n'a pas d'utility natif. Solutions : (1) syntaxe arbitraire CSS via plugin custom matchUtilities({ 'vt-name': v => ({ viewTransitionName: v }) }), (2) style={{ viewTransitionName: 'card' }} en React, (3) classe arbitraire via [view-transition-name:card]. La View Transitions API ouvre des animations spectaculaires entre routes.
Explication :
Preflight (basé sur modern-normalize) reset agressivement : marges 0, listes sans bullets, headings sans poids, etc. Avantage : tu pars d'une base propre, le design vient des classes Tailwind. Inconvénient : intégrer Tailwind dans un site WordPress ou un CMS existant peut casser le rendu. Solution :
corePlugins: { preflight: false } ou utiliser @tailwindcss/typography via prose sur les zones rich-text.
Astuce : sur un projet hybride, scope Tailwind à un wrapper (
.app) via PostCSS plugin tailwindcss-scoped-preflight.
Explication :
Pattern shadcn/ui : const button = cva('inline-flex rounded font-medium', {
variants: {
intent: { primary: 'bg-blue-500', secondary: 'bg-gray-200' },
size: { sm: 'px-2 py-1 text-sm', lg: 'px-4 py-3 text-lg' }
},
defaultVariants: { intent: 'primary', size: 'sm' }
});Usage :
button({ intent: 'secondary', size: 'lg' }). Types TypeScript autocomplétés. Combine avec cn() pour merger.
Alternative :
tailwind-variants (avec slots, compoundVariants) ou tv().
Explication :
forced-colors:* applique des styles si l'utilisateur a activé un mode haute lisibilité (Windows High Contrast Mode). Important pour l'accessibilité. Pattern : retirer ombres/dégradés et forcer des bordures explicites en mode forced-colors. contrast-more: et contrast-less: existent aussi pour prefers-contrast.
Explication :
Pipeline RTL avec Tailwind : (1) remplacer
pl/pr par ps/pe, ml/mr par ms/me, text-left/right par text-start/end ; (2) définir dir="rtl" côté serveur selon la locale ; (3) pour les exceptions (chevrons, icônes), utiliser rtl:rotate-180. Le mécanisme inversion automatique fonctionne sans JS supplémentaire.
Tester dès le début avec
dir="rtl" sur la home — beaucoup de bugs RTL viennent d'icônes oubliées.
Explication :
writing-mode n'a pas d'utility par défaut (peu commun). Solutions : (1) syntaxe arbitraire [writing-mode:vertical-rl], (2) plugin custom avec addUtilities. Cas d'usage : header asiatique, design éditorial vertical. À ne pas confondre avec rotate-90 (rotation visuelle, pas vraie écriture verticale).
Explication :
OKLCH (Oklab + LCH) est l'espace de couleurs perceptuel le plus précis disponible en CSS. Avantages concrets : (1)
red-500 et blue-500 ont la même luminosité perçue ; (2) color-mix(in oklch, red, blue) produit des violets vifs (vs grisâtres en sRGB) ; (3) Les nouvelles couleurs P3 et Display HDR sont natives. Tailwind v4 expose --color-blue-500: oklch(...).
Outil utile :
oklch.com pour visualiser et générer des palettes.
Explication :
clip-path n'a pas d'utility natif (formes trop variées). Solution : syntaxe arbitraire [clip-path:circle(50%)], ou définir un set dans theme.extend.clipPath + plugin avec matchUtilities.
Explication :
L'API plugin accepte les
@keyframes via addUtilities ou addBase. Bonne pratique : encapsuler keyframe + utility dans le même plugin pour la portabilité. Alternative : ajouter dans theme.extend.keyframes + theme.extend.animation, plus déclaratif.
Pour des animations complexes (timelines, scroll-driven), un plugin custom est souvent plus lisible que
theme.extend.
Explication :
aria-[expanded=true]:rotate-180 compile en [aria-expanded="true"] { transform: rotate(180deg); }. Tailwind expose aussi des shorthands : aria-expanded:, aria-checked:, aria-disabled:... pour les attributs ARIA courants. Indispensable pour styler les composants accessibles (accordéons, dropdowns).
Explication :
// CSS
:root { --color-primary: 220 90% 50%; }
[data-theme="dark"] { --color-primary: 220 90% 70%; }
[data-theme="sunset"] { --color-primary: 20 90% 50%; }
// tailwind.config.js
colors: { primary: 'hsl(var(--color-primary) / <alpha-value>)' }
// JS
document.documentElement.dataset.theme = 'sunset';Permet thèmes infinis, switch instantané, sans rebuild. Pattern Vercel, Linear, Notion.
Astuce
<alpha-value> : la syntaxe hsl(var(--color-primary) / <alpha-value>) permet à Tailwind d'injecter l'alpha (bg-primary/50).
Explication :
safelist: ['bg-red-500', { pattern: /bg-(red|green|blue)-(500|700)/ }]. Utile quand : (1) tu construis des classes en string ('bg-' + color), (2) tu rends du contenu Markdown dynamique, (3) classes injectées par une lib tierce. Attention : les regex permissifs explosent le CSS — toujours scoper variants: [...] aussi pour limiter.
Toujours préférer une approche statique (mapper les classes possibles) à la safelist, sauf cas vraiment dynamique.
Explication :
@apply est OK pour quelques composants très répétés (.btn, .card). À l'échelle (50+ composants), recréer du CSS sémantique annule l'intérêt utility-first. Préférer : composants framework (React/Vue) + cva, ou utility classes nues directement dans le HTML. La doc Tailwind elle-même conseille de l'éviter par défaut.
Test simple : si tu te retrouves à écrire
.btn-primary { @apply btn bg-blue-500; } et .btn-primary-large { @apply btn-primary px-6; } → tu as recréé une cascade CSS qui aurait dû rester en HTML.
Explication :
scroll-mt-{n} = scroll-margin-top. Utile pour les ancres internes : si tu as un header sticky de 64px, ajoute scroll-mt-16 sur les sections cibles pour qu'elles ne se cachent pas sous le header au scroll. Aussi : scroll-mb-*, scroll-mx-*, scroll-pt-*, scroll-pb-*...
Explication :
Le Shadow DOM isole le CSS : les classes
.bg-blue-500 du document ne s'appliquent pas dans le shadow root. Pattern moderne : const sheet = new CSSStyleSheet();
sheet.replaceSync(tailwindCompiledCSS);
shadowRoot.adoptedStyleSheets = [sheet];Ou utiliser ::part() pour exposer des éléments stylables depuis le parent.
Lit, Stencil, FAST proposent des intégrations Tailwind documentées avec ce pattern.
Explication :
Container queries Tailwind v3.2+ : <div class="@container">
<div class="@md:flex @lg:grid-cols-3">...</div>
</div>Les breakpoints
@sm, @md, @lg, @xl, @2xl sont définissables dans la config. Le composant s'adapte à la largeur de son slot, pas de la fenêtre — révolutionnaire pour des layouts vraiment composables.
Explication :
Pattern CI : ajouter
size-limit dans package.json avec un seuil sur le CSS final. La PR échoue si le bundle gonfle anormalement. Empêche les régressions silencieuses (ajout d'un plugin, safelist trop permissive). Compatible GitHub Actions, GitLab CI, Vercel checks.
Combine avec
tailwind-config-viewer en local pour explorer les classes générées.
Explication :
Tailwind v3 ne fournit que les linear gradients (
bg-gradient-to-{dir}). Pour conic-gradient et radial-gradient : syntaxe arbitraire ou plugin. Tailwind v4 ajoute bg-linear-{dir}, bg-radial, bg-conic nativement avec position et stops déclarables.
Explication :
Pattern obligatoire pour SSR/SSG : <head>
<script>
(function() {
var stored = localStorage.getItem('theme');
if (stored === 'dark' || (!stored && matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
}
})();
</script>
</head>Doit être inline et synchrone, exécuté avant le premier paint. Sinon : flash blanc puis transition vers sombre = expérience cassée.
Next.js avec
next-themes automatise ce pattern. Astro et Remix proposent des helpers similaires.
Explication :
Tailwind v4 introduit
@theme en CSS (config CSS-first) : @theme {
--color-brand-500: oklch(0.7 0.2 200);
--font-display: "Inter Variable";
--spacing-tight: 0.5rem;
}Variables CSS auto-traduites en utilities (bg-brand-500, font-display, p-tight). Plus besoin de tailwind.config.js, ou tu peux le coupler.
Explication :
Migration officielle documentée :
npx @tailwindcss/upgrade automatise une partie. Points d'attention : nommage gradients, suppression de variants explicites (tout est dispo par défaut), config CSS-first (mais @config permet de garder l'ancienne), nouveau pipeline build (Vite/PostCSS plugin v4). Test visuel obligatoire car OKLCH change subtilement les teintes.
Pour minimiser le risque : audit visuel avec Chromatic/Percy avant et après migration sur un environnement staging.
Explication :
contain n'a pas d'utility natif (peu commun). Pour des optimisations perf (forcer un sous-arbre à se rendre indépendamment), utiliser la syntaxe arbitraire ou un plugin custom. À combiner avec content-visibility et contain-intrinsic-size pour des listes très longues.
Explication :
Tailwind est style-only. Il aide via :
sr-only, focus-visible:, motion-reduce:, contrast-more:, forced-colors:, échelle d'intensités calibrée pour le contraste (mais pas une garantie WCAG AA — à vérifier). Pour la vraie a11y comportementale (focus trap, aria-live, keyboard nav) : combiner avec Radix UI, React Aria, Headless UI. Tailwind seul ne fait pas un bouton accessible.
Combo recommandé pour app a11y-critique : Radix UI (logique) + Tailwind (style) + axe-core (audit) + Storybook + screen reader tests manuels.
Explication :
user-invalid:* applique le style uniquement si l'utilisateur a interagi avec l'input et qu'il est invalide. Évite de marquer en rouge un input vide à l'arrivée sur la page. invalid:* existe aussi mais s'applique dès le rendu si vide+required. user-valid:* est le pendant.
Explication :
eslint-plugin-tailwindcss détecte : (1) classes inexistantes, (2) ordre non standard, (3) classes dupliquées/conflictuelles, (4) usage abusif de valeurs arbitraires (p-[13px]) hors du design system. Configurer en CI avec règle no-arbitrary-value = zéro tolérance pour les hacks pixel.
Combo :
eslint-plugin-tailwindcss + prettier-plugin-tailwindcss (tri auto des classes) = qualité maintenue automatiquement.
Explication :
prettier-plugin-tailwindcss est le plugin officiel : trie automatiquement les classes Tailwind dans l'ordre canonique (layout → spacing → typography → colors...). Améliore la lisibilité, réduit les diffs Git inutiles. Compatible JSX, Vue, Svelte, Astro, Twig, ERB, Liquid via configuration.
Explication :
Angular utilise webpack/esbuild + PostCSS — Tailwind s'intègre nativement. Pièges : (1) le scanner Tailwind doit voir les fichiers
.ts (templates inline), (2) attention au ViewEncapsulation : None permet aux classes Tailwind de s'appliquer aux templates externes ; en Emulated, les classes du HTML enfant sont préservées si elles existent dans le HTML scanné. (3) Combiner avec Angular CDK pour la logique, Tailwind pour le style.
Pour Angular SSR : same FOUC pattern que React/Next — script blocking en
<head> pour le mode sombre.
Explication :
Tailwind v4 introduit
mask-{type}, mask-linear-{dir}, mask-radial, mask-no-repeat... pour des effets de fade, vignettage, formes complexes. v3 : syntaxe arbitraire [mask-image:linear-gradient(to_right,black,transparent)] ou plugin custom.
Explication :
Pattern : theme: {
extend: {
fontSize: {
'fluid-xl': ['clamp(1.5rem, 2.5vw, 2.5rem)', '1.2'],
'fluid-lg': ['clamp(1.125rem, 1.5vw, 1.5rem)', '1.4']
}
}
}Usage :
text-fluid-xl. La syntaxe [fontSize, lineHeight] est supportée. clamp(min, ideal, max) élimine les breakpoints typo.
Outil utile :
utopia.fyi/type pour générer des échelles fluides.
Explication :
backdrop-saturate-{n} applique backdrop-filter: saturate(...). Échelle : 0, 50, 100, 150, 200. Combinable avec backdrop-blur, backdrop-brightness, backdrop-contrast... pour des effets glassmorphism complets.
Explication :
Panda CSS (Chakra) : atomic CSS-in-JS, types-first, runtime-free, intégré à Vite/Next, idéal pour TypeScript-heavy. UnoCSS (Anthony Fu) : engine ultra-flexible, attributify mode (
<div bg="blue-500" p="4">), presets composables, build le plus rapide. Tailwind : écosystème dominant, plugins matures (forms, typography, container queries), community immense, support enterprise. Choix selon contexte d'équipe et écosystème.
En entretien : ne pas dénigrer un outil. Tous trois sont valables. Le bon choix dépend du tooling existant et de l'expertise de l'équipe.
Explication :
peer sur le sibling source, peer-hover:*, peer-checked:*, peer-focus:* sur les frères suivants. Limitation : ne fonctionne que vers l'aval (sibling après dans le DOM). Pour styler un parent, utiliser has-[].
Explication :
En Tailwind v4, toutes les couleurs par défaut sont déjà en OKLCH (
--color-blue-500: oklch(...)). Aucune action requise. Tu peux mélanger formats (#hex, rgb(), oklch()) — Tailwind les expose tels quels via les CSS variables.
Explication :
Stratégie d'introduction progressive : // tailwind.config.js
module.exports = {
important: '.tw',
corePlugins: { preflight: false }
}
// HTML
<div class="tw">
<button class="px-4 py-2 bg-blue-500">Migré</button>
</div>Empêche les conflits avec le CSS legacy. Quand la majorité est migrée, simplifier la config.
Astuce : ajouter
postcss-prefix-selector pour scoper tout Tailwind à .tw automatiquement.
Explication :
L'API drag-and-drop n'expose pas de pseudo-class CSS native pour
dragover. Solution : gérer un attribut/data dans le JS lors des events dragenter/dragleave, puis cibler avec data-[dragover=true]:bg-blue-100. Pattern utilisé par les uploaders de Vercel, Stripe, Linear.
Explication :
Tailwind brille avec RSC : pas de CSS-in-JS runtime, donc pas de coût d'hydratation. Patterns : (1) utility classes nues dans les RSC, (2)
cn() pour conditionnels statiques, (3) thème via cookies + CSS variables (le serveur lit le cookie et applique le bon theme dès le HTML), (4) éviter useTheme() hook qui force le client.
Voir
next-themes v0.3+ qui supporte App Router et écrit les CSS vars au niveau du <html> en SSR (sans flash).
Explication :
Tailwind n'a pas de stagger natif. Solutions : (1)
nth-1:delay-100 nth-2:delay-200 ... pour quelques enfants, (2) Framer Motion / Motion One / GSAP pour des animations orchestrées (variants, stagger), (3) JS pur avec animationDelay = i * 100ms. Pour beaucoup d'éléments, JS reste plus pratique.
Explication :
Adam Wathan (créateur Tailwind) explique :
@apply a été initialement créé comme escape hatch, pas comme pattern principal. Pour des composants vraiment réutilisables, un composant React/Vue/Angular avec props typés (souvent via cva ou tailwind-variants) est strictement supérieur : composabilité, réutilisation, typage, autocomplete. @apply est OK pour de vraies primitives CSS (.prose, .markdown-body) mais pas pour des composants UI.
À retenir : si tu peux écrire un composant, fais-le.
@apply reste pour les cas où un composant est impossible (CSS pur, Markdown, contenus injectés en HTML).
Explication :
Syntaxe arbitraire avec underscore en lieu d'espace (Tailwind les convertit) :
bg-[color-mix(in_oklch,blue,red_50%)]. Utile pour des couleurs dynamiques calculées sans plugin. Les fonctions CSS modernes (color-mix, oklch(), color()) sont utilisables ainsi.
Explication :
Pipeline a11y complet pour app Tailwind : (automatique) axe-core en CI/CD bloque les régressions évidentes, Lighthouse en preview, Storybook addon-a11y sur chaque composant ; (manuel) tests lecteur d'écran (au moins NVDA + VoiceOver mobile), navigation clavier complète (Tab, Shift-Tab, Esc), modes haute contraste (forced-colors). Pour les contrastes, axe-core détecte 60% des cas — le reste se vérifie à l'œil + outils.
Tailwind facilite (intensités calibrées,
focus-visible:, sr-only, motion-reduce:) mais ne remplace pas une démarche a11y rigoureuse. La sémantique HTML + ARIA reste à 100% sur le développeur.
Explication :
scrollbar-gutter n'a pas d'utility par défaut. Syntaxe arbitraire [scrollbar-gutter:stable]. Utile pour empêcher le contenu de sauter quand un scroll apparaît/disparaît (modals, contenus dynamiques). À envisager dans un plugin custom si l'app en utilise beaucoup.
Explication :
Conventions saines à grande échelle : (1) ordre canonique appliqué automatiquement par
prettier-plugin-tailwindcss, (2) dès qu'un élément dépasse ~10 classes, extraire dans cva ou un objet de classes nommées, (3) pour des composants vraiment partagés, créer un <Card> avec props typés, (4) documenter dans Storybook les variants disponibles. La lisibilité est un investissement de design system, pas un détail.
À éviter absolument : mélanger ordre des classes selon l'inspiration du moment (un fichier git diff devient illisible). Le tri auto via Prettier supprime ce débat.