Astuces Tailwind CSS avec Claude Code : guide pratique pour une UI stable
Utiliser Tailwind CSS avec Claude Code : tokens, responsive, mode sombre, composants, safelist et tests visuels React.
Tailwind CSS permet de construire une interface avec de petites classes utilitaires comme p-4, grid, text-sm ou rounded-lg. Claude Code accélère ce travail, car il peut lire le dépôt, modifier des fichiers, lancer des commandes et revoir les changements. Le risque, c’est qu’une amélioration rapide laisse aussi des className interminables, des couleurs incohérentes, des bugs uniquement sur mobile, un mode sombre incomplet ou des classes dynamiques absentes du CSS de production.
Ce guide propose un workflow concret pour utiliser Claude Code avec Tailwind CSS sans transformer la UI en accumulation de rustines. Nous couvrons les design tokens, les utilitaires responsive, l’extraction de composants, l’évitement du class soup, le mode sombre, les formulaires, boutons, cartes, safelist, content scanning et vérifications visuelles avec Playwright. Les exemples sont en React + TypeScript, mais la logique s’applique aussi à Astro, Next.js, Remix ou Vite.
Les références officielles utilisées sont Tailwind Theme variables, Responsive design, Dark mode, Detecting classes in source files et Adding custom styles. Pour React typé, consultez le guide React TypeScript. Pour Claude Code, utilisez la documentation officielle. Pour les screenshots comparatifs, voir Playwright Visual comparisons.
Si vos prompts restent trop vagues, commencez par 5 astuces pour de meilleurs prompts. Pour une expérience mobile complète, complétez avec le guide PWA.
Commencer par un audit
Ne demandez pas à Claude Code de modifier les fichiers tout de suite. Dans un projet Tailwind, les problèmes viennent souvent de petites décisions dispersées : couleurs, espacements, breakpoints, états de formulaire, mode sombre, CTA, blocs publicitaires et blocs de code. Demandez d’abord un rapport.
Analyse l'usage de Tailwind CSS dans ce dépôt. Ne modifie pas encore les fichiers.
Produit un rapport sur :
- Les design tokens existants : couleurs, spacing, radius, shadow, typography
- Les composants React avec des className trop longs
- Les layouts fragiles en 375px, 768px ou 1440px
- Les zones couvertes ou non par le mode sombre
- Les styles répétés dans forms, buttons, cards et badges
- Les classes Tailwind dynamiques à risque en CSS de production
- Les vérifications visuelles existantes : Playwright, Storybook ou navigateur
Sur ClaudeCodeLab, Masa a déjà réduit un espacement en regardant seulement le desktop. Sur mobile, le CTA de fin d’article et l’emplacement publicitaire se sont retrouvés trop proches. Une classe Tailwind semble locale, mais elle peut toucher la lecture et la conversion.
Centraliser les design tokens
Un design token est une valeur de design nommée : couleur, espacement, police, rayon, ombre ou breakpoint. Tailwind CSS v4 utilise @theme, qui crée des utilitaires comme bg-brand-600 ou rounded-card. Dans un projet plus ancien avec tailwind.config.ts, placez les mêmes valeurs dans theme.extend.
/* src/styles/app.css */
@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
@theme {
--font-sans: Inter, system-ui, sans-serif;
--color-brand-50: #eef6ff;
--color-brand-100: #d9ebff;
--color-brand-600: #2563eb;
--color-brand-700: #1d4ed8;
--color-ink: #111827;
--color-muted: #6b7280;
--color-surface: #ffffff;
--color-danger: #dc2626;
--radius-card: 0.75rem;
--shadow-card: 0 16px 40px rgb(15 23 42 / 0.08);
}
Une bonne consigne à Claude Code est : “Utilise les tokens existants brand, surface et danger; ajoute une variable @theme uniquement si la valeur sera réutilisée.” C’est plus fiable que “rends le bleu plus joli”, qui finit souvent par mélanger blue, sky et indigo.
Raisonner mobile-first
Tailwind est mobile-first : la classe de base s’applique aux petits écrans, puis sm:, md: et lg: ajoutent les différences pour les écrans plus larges. Si l’on part du desktop, on corrige souvent le mobile trop tard.
Améliore la grille produit avec Tailwind CSS.
Contraintes :
- 1 colonne en 375px, 2 colonnes dès 640px, 3 colonnes dès 1024px
- Images carrées
- Hauteur des cartes cohérente
- Bouton CTA aligné en bas
- Ne change pas le type Product
- Vérifie les screenshots mobile et desktop après modification
type Product = {
id: string;
name: string;
price: number;
imageUrl: string;
};
export function ProductGrid({ products }: { products: Product[] }) {
return (
<section className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
{products.map((product) => (
<article
key={product.id}
className="flex h-full flex-col overflow-hidden rounded-card border border-slate-200 bg-surface shadow-card dark:border-slate-800 dark:bg-slate-950"
>
<img
src={product.imageUrl}
alt={product.name}
className="aspect-square w-full object-cover"
/>
<div className="flex flex-1 flex-col p-4">
<h3 className="line-clamp-2 text-base font-semibold text-ink dark:text-white">
{product.name}
</h3>
<p className="mt-2 text-sm text-muted dark:text-slate-400">
{product.price.toLocaleString("fr-FR", {
style: "currency",
currency: "EUR",
})}
</p>
<button className="mt-auto rounded-lg bg-brand-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-brand-700 focus:outline-none focus:ring-2 focus:ring-brand-600 focus:ring-offset-2 dark:focus:ring-offset-slate-950">
Voir le détail
</button>
</div>
</article>
))}
</section>
);
}
aspect-square stabilise l’image, flex h-full flex-col stabilise la carte, et mt-auto garde le CTA en bas.
Extraire avant le class soup
Le class soup apparaît lorsqu’un className devient si long qu’on ne distingue plus la base, la variante et le correctif ponctuel. Il n’est pas nécessaire de tout cacher avec @apply; extrayez surtout les boutons, cartes, badges et champs répétés.
import type { ButtonHTMLAttributes, ReactNode } from "react";
type ButtonVariant = "primary" | "secondary" | "danger";
const buttonVariants: Record<ButtonVariant, string> = {
primary: "bg-brand-600 text-white hover:bg-brand-700 focus:ring-brand-600",
secondary:
"border border-slate-300 bg-white text-slate-900 hover:bg-slate-50 focus:ring-slate-400 dark:border-slate-700 dark:bg-slate-900 dark:text-white",
danger: "bg-danger text-white hover:bg-red-700 focus:ring-danger",
};
function cn(...classes: Array<string | false | null | undefined>) {
return classes.filter(Boolean).join(" ");
}
type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
variant?: ButtonVariant;
loading?: boolean;
children: ReactNode;
};
export function Button({
variant = "primary",
loading = false,
disabled,
className,
children,
...props
}: ButtonProps) {
return (
<button
className={cn(
"inline-flex min-h-10 items-center justify-center rounded-lg px-4 py-2 text-sm font-semibold transition focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60 dark:focus:ring-offset-slate-950",
buttonVariants[variant],
className,
)}
disabled={disabled || loading}
{...props}
>
{loading ? "Traitement..." : children}
</button>
);
}
Gardez les classes sous forme de chaînes complètes. bg-${color}-600 peut échapper au scanner de Tailwind.
Mode sombre, formulaires et CTA ensemble
Le mode sombre ne se limite pas au fond. Il faut vérifier texte, bordures, ombres, champs, erreurs, état désactivé et focus ring. Les formulaires et CTA méritent une attention particulière parce qu’ils portent les demandes, téléchargements et ventes.
"use client";
import type { FormEvent } from "react";
type LeadFormProps = {
onSubmit: (values: { email: string; message: string }) => void;
error?: string;
};
export function LeadForm({ onSubmit, error }: LeadFormProps) {
function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const formData = new FormData(event.currentTarget);
onSubmit({
email: String(formData.get("email") ?? ""),
message: String(formData.get("message") ?? ""),
});
}
return (
<form
onSubmit={handleSubmit}
className="space-y-4 rounded-card border border-slate-200 bg-white p-5 shadow-card dark:border-slate-800 dark:bg-slate-950"
>
<label className="block text-sm font-medium text-slate-900 dark:text-white">
Email
<input
name="email"
type="email"
required
aria-describedby={error ? "lead-form-error" : undefined}
className="mt-1 w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 outline-none focus:border-brand-600 focus:ring-2 focus:ring-brand-600/20 dark:border-slate-700 dark:bg-slate-900 dark:text-white"
placeholder="you@example.com"
/>
</label>
{error ? (
<p id="lead-form-error" className="text-sm font-medium text-danger">
{error}
</p>
) : null}
<button className="w-full rounded-lg bg-brand-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-brand-700 focus:outline-none focus:ring-2 focus:ring-brand-600 focus:ring-offset-2 dark:focus:ring-offset-slate-950">
Demander un accompagnement
</button>
</form>
);
}
Pour la revue accessibilité, reliez ce travail au guide accessibilité Claude Code.
Safelist et content scanning
Tailwind génère le CSS à partir des classes détectées dans les fichiers source. Une classe construite dynamiquement peut manquer en production. Préférez une map statique.
type Status = "success" | "warning" | "danger";
const statusClasses: Record<Status, string> = {
success: "bg-emerald-50 text-emerald-700 ring-emerald-600/20",
warning: "bg-amber-50 text-amber-800 ring-amber-600/20",
danger: "bg-red-50 text-red-700 ring-red-600/20",
};
export function StatusBadge({ status, label }: { status: Status; label: string }) {
return (
<span
className={`inline-flex items-center rounded-full px-2.5 py-1 text-xs font-semibold ring-1 ring-inset ${statusClasses[status]}`}
>
{label}
</span>
);
}
Si des classes viennent d’un package externe ou d’un CMS, utilisez @source ou @source inline() avec retenue.
@import "tailwindcss";
@source "../node_modules/@acme/ui-kit";
@source inline("bg-emerald-50");
@source inline("text-emerald-700");
@source inline("bg-amber-50");
@source inline("text-amber-800");
Vérifier par screenshot
Un build réussi ne garantit pas une UI correcte. Avec Playwright, capturez les largeurs importantes.
import { expect, test } from "@playwright/test";
const viewports = [
{ name: "mobile", size: { width: 375, height: 812 } },
{ name: "tablet", size: { width: 768, height: 1024 } },
{ name: "desktop", size: { width: 1440, height: 960 } },
];
for (const viewport of viewports) {
test(`pricing page visual check - ${viewport.name}`, async ({ page }) => {
await page.setViewportSize(viewport.size);
await page.goto("/pricing");
await expect(page.getByRole("main")).toHaveScreenshot(
`pricing-${viewport.name}.png`,
{ maxDiffPixelRatio: 0.01 },
);
});
}
Demandez ensuite à Claude Code la liste des fichiers modifiés, les risques, les largeurs vérifiées, le mode sombre et la procédure manuelle si Playwright n’existe pas.
Cas d’usage et pièges
| Cas d’usage | Demande à Claude Code | Point Tailwind |
|---|---|---|
| Landing page | Revoir hero, CTA, prix et témoignages ensemble | Espacement, hiérarchie, CTA |
| Dashboard SaaS | Organiser tables, filtres, sidebar, empty states | Densité, overflow, sticky header |
| Formulaire | Couvrir input, erreur, succès, loading, disabled | Focus ring, labels, cible mobile |
| Site éditorial | Revoir texte, code, publicité et CTA | Largeur de lecture, scroll code, liens |
Les pièges courants sont les classes dynamiques, la revue desktop uniquement, le mode sombre partiel, l’abus de @apply, les couleurs presque identiques, les erreurs de formulaire oubliées et l’absence de screenshots.
CTA et résultat testé
Une amélioration Tailwind doit soutenir le parcours business : CTA de fin d’article, carte de prix, formulaire de consultation ou téléchargement. ClaudeCodeLab propose une cheatsheet Claude Code gratuite, des produits et templates et de la formation ou consultation pour les équipes.
Après test sur des pages ClaudeCodeLab, les deux actions les plus utiles ont été l’audit initial et la capture 375px. Elles ont révélé des problèmes d’espacement de CTA, de focus formulaire et de contraste en mode sombre avant que cela ne devienne une autre session de nettoyage de className.
PDF gratuit: cheatsheet Claude Code
Saisissez votre email et téléchargez une page avec commandes, habitudes de review et workflow sûr.
Nous protégeons vos données et n'envoyons pas de spam.
À propos de l'auteur
Masa
Ingénieur spécialisé dans les workflows pratiques avec Claude Code.
Articles liés
Échelle de sécurité des permissions Claude Code
Passer du read-only aux éditions limitées, preuves et checks de déploiement sans perdre le contrôle.
Claude Code Small PR Proof Pack : rendre les petits changements reviewables
Un pack de preuve pour PR Claude Code : diff, vérifications, URL publique, CTA et rollback.
Gate de review avant commit avec Claude Code
Review avant commit avec Claude Code : diff, build, URL publique, liens Gumroad, CTA consultation, tests manquants et fichiers hors scope.