CSS de production avec Claude Code : couches, tokens et regression visuelle
Utilisez Claude Code pour fiabiliser le CSS avec couches, tokens, container queries, mode sombre et tests visuels.
CSS semble etre un domaine facile a confier a Claude Code, mais en production il ne suffit pas que l’ecran ressemble a la maquette. Il faut une architecture CSS, des tokens reutilisables, du responsive, un mode sombre, de l’accessibilite et des tests de regression. Ce guide part d’une carte de prix pour montrer comment laisser Claude Code avancer vite sans transformer les styles en empilement de correctifs.
Un design token est une decision nommee : couleur, espacement, rayon ou ombre. Les cascade layers sont des etageres de priorite pour les regles CSS. Les container queries changent le composant selon la largeur de son conteneur, pas seulement selon le viewport. Quand ces definitions sont explicites dans le prompt, Claude Code respecte mieux le systeme existant.
Gardez les references officielles sous la main : MDN @layer, CSS custom properties, container queries, prefers-color-scheme, contraste W3C et comparaisons visuelles Playwright. Pour Claude Code, partez de Claude Code common workflows. Cote site, lisez aussi les bonnes pratiques CLAUDE.md et les strategies de test.
Poser le cadre avant d’editer
Si vous demandez seulement “nettoie le CSS”, Claude Code peut ameliorer l’ecran actuel tout en rendant le futur plus fragile. Un vrai projet melange souvent CSS global, CSS Modules, utilitaires Tailwind, styles Markdown, valeurs par defaut du navigateur et anciens overrides. Sans limite, l’assistant ajoute des selecteurs plus forts au lieu de corriger l’architecture.
Le premier objet a creer est le harness, c’est-a-dire le cadre de travail sur pour l’agent. Indiquez les fichiers cibles, les couches autorisees, les noms de tokens, les commandes a lancer et les zones interdites. Comme les changements CSS donnent un retour visuel immediat, ils grossissent vite. Le cadre garde le diff lisible.
flowchart LR
P["Prompt Claude Code"] --> L["cascade layers"]
L --> T["design tokens"]
T --> C["card/button components"]
C --> R["responsive + container queries"]
R --> D["dark mode"]
D --> A["accessibility checks"]
A --> V["Playwright visual regression"]
Commencez par une inspection :
Read AGENTS.md, CLAUDE.md, package.json, and every file under src/styles.
Do not edit yet.
Report the current CSS architecture, naming conventions, token usage,
dark-mode strategy, responsive breakpoints, and test commands.
Then propose the smallest safe plan for a pricing card and CTA button.
Ce prompt evite que Claude Code ignore les conventions existantes. La decision humaine porte sur la frontiere : quel composant changer, quelles regles conserver et quelles commandes prouvent le resultat.
Verrouiller la priorite avec les cascade layers
La cascade decide quelle regle gagne lorsque plusieurs regles touchent le meme element. Augmenter la specificite, importer plus tard ou ajouter !important debloque parfois une release, mais complique la suivante. @layer rend les priorites explicites.
/* src/styles/app.css */
@layer reset, tokens, base, components, utilities, overrides;
@import "./tokens.css" layer(tokens);
@import "./base.css" layer(base);
@import "./components.css" layer(components);
@import "./utilities.css" layer(utilities);
@layer reset {
*,
*::before,
*::after {
box-sizing: border-box;
}
body,
h1,
h2,
h3,
p {
margin: 0;
}
}
@layer overrides {
.legacy-markdown :where(table, pre) {
max-width: 100%;
}
}
Demandez a Claude Code de classer avant de reecrire :
Move existing global CSS into the layer model in src/styles/app.css.
Do not change class names used by templates.
Use reset, tokens, base, components, utilities, and overrides only.
If a rule must go into overrides, explain why in the final response.
Run npm test and the visual check command after editing.
Le piege est la migration partielle. Une regle hors couche peut encore l’emporter sur une regle dans une couche, et @import doit rester place correctement. Testez d’abord un nouveau composant, puis migrez l’ancien CSS par petites etapes.
Stocker les decisions dans des tokens CSS
Les tokens empechent Claude Code d’inventer une nouvelle couleur, une nouvelle ombre et un nouveau rayon a chaque demande. En pratique, ce sont des variables CSS pour les decisions que l’equipe veut reutiliser.
/* src/styles/tokens.css */
@layer tokens {
:root {
color-scheme: light;
--color-bg: #f7f7f2;
--color-surface: #ffffff;
--color-text: #1f2933;
--color-muted: #5d6673;
--color-border: #d9ded7;
--color-accent: #0f766e;
--color-accent-strong: #0b4f49;
--color-focus: #b45309;
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-3: 0.75rem;
--space-4: 1rem;
--space-6: 1.5rem;
--space-8: 2rem;
--radius-sm: 0.25rem;
--radius-md: 0.5rem;
--shadow-card: 0 0.75rem 2rem rgb(31 41 51 / 0.12);
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
color-scheme: dark;
--color-bg: #111827;
--color-surface: #1f2937;
--color-text: #f9fafb;
--color-muted: #cbd5e1;
--color-border: #475569;
--color-accent: #2dd4bf;
--color-accent-strong: #99f6e4;
--color-focus: #fbbf24;
--shadow-card: 0 0.75rem 2rem rgb(0 0 0 / 0.32);
}
}
:root[data-theme="dark"] {
color-scheme: dark;
--color-bg: #111827;
--color-surface: #1f2937;
--color-text: #f9fafb;
--color-muted: #cbd5e1;
--color-border: #475569;
--color-accent: #2dd4bf;
--color-accent-strong: #99f6e4;
--color-focus: #fbbf24;
--shadow-card: 0 0.75rem 2rem rgb(0 0 0 / 0.32);
}
}
En production, demandez une verification de contraste. WCAG attend generalement un ratio d’au moins 4.5:1 pour le texte courant. Il faut regarder le texte principal, le texte secondaire, les liens, les boutons et le focus ring dans les deux themes.
Garder carte et bouton dans la couche composant
Les cartes et les boutons apparaissent partout : blog, dashboard SaaS, landing page, reglages et paiement. Si Claude Code ajoute des classes generiques comme .title ou .button, une autre page cassera un jour. Utilisez des noms qui montrent la responsabilite.
/* src/styles/components.css */
@layer components {
.ui-card {
container: card / inline-size;
display: grid;
gap: var(--space-4);
padding: var(--space-6);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
background: var(--color-surface);
color: var(--color-text);
box-shadow: var(--shadow-card);
}
.ui-card__eyebrow {
color: var(--color-accent);
font-size: 0.875rem;
font-weight: 700;
}
.ui-card__title {
max-width: 18ch;
font-size: clamp(1.5rem, 1rem + 2cqi, 2.25rem);
line-height: 1.1;
}
.ui-card__body {
color: var(--color-muted);
font-size: 1rem;
line-height: 1.7;
}
.ui-actions {
display: flex;
flex-wrap: wrap;
gap: var(--space-3);
align-items: center;
}
.ui-button {
display: inline-flex;
min-height: 2.75rem;
align-items: center;
justify-content: center;
padding: 0.75rem 1rem;
border: 1px solid transparent;
border-radius: var(--radius-sm);
background: var(--color-accent);
color: var(--color-surface);
font: inherit;
font-weight: 700;
text-decoration: none;
}
.ui-button:hover {
background: var(--color-accent-strong);
}
.ui-button:focus-visible {
outline: 3px solid var(--color-focus);
outline-offset: 3px;
}
.ui-button[aria-disabled="true"],
.ui-button:disabled {
cursor: not-allowed;
opacity: 0.55;
}
}
Ajoutez une page de previsualisation stable, par exemple /style-lab, afin que Playwright cible toujours le meme contenu.
<main class="style-lab">
<article class="ui-card" data-testid="pricing-card">
<p class="ui-card__eyebrow">Team plan</p>
<h1 class="ui-card__title">Ship production CSS with Claude Code</h1>
<p class="ui-card__body">
Layer your CSS, reuse tokens, check dark mode, and catch visual regressions before release.
</p>
<div class="ui-actions">
<a class="ui-button" href="/training/">Start with training</a>
<a class="ui-button" href="/thanks/">Get the free checklist</a>
</div>
</article>
</main>
Responsive et container queries
Les media queries reagissent au viewport. Les container queries reagissent a l’espace reel du composant. C’est crucial quand la meme carte apparait dans une page large, une sidebar et une grille de dashboard.
/* src/styles/responsive.css */
@layer components {
.style-lab {
display: grid;
min-height: 100svh;
place-items: center;
padding: clamp(1rem, 4vw, 4rem);
background: var(--color-bg);
}
.pricing-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
gap: var(--space-6);
width: min(100%, 72rem);
}
@container card (min-width: 36rem) {
.ui-card {
grid-template-columns: 1fr auto;
align-items: center;
}
.ui-card__body {
max-width: 58ch;
}
.ui-actions {
justify-content: end;
}
}
@media (max-width: 40rem) {
.ui-card {
padding: var(--space-4);
}
.ui-actions,
.ui-button {
width: 100%;
}
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
scroll-behavior: auto !important;
transition-duration: 0.001ms !important;
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
}
}
}
Ne dites pas seulement “rends-le responsive”. Demandez de verifier 320, 375, 768, 1024 et 1440 px : overflow horizontal, retours a la ligne, focus ring, hauteur du CTA et mode sombre.
Cas d’usage concrets
Premier cas : une carte de monetisation pour blog ou landing. Publicite, affiliation, consulting et telechargement gratuit doivent etre visibles sans provoquer de layout shift. Demandez le CSS composant, le responsive, le mode sombre et le test visuel dans un petit changement.
Deuxieme cas : un ecran de reglages SaaS. Cartes, formulaires, actions dangereuses et etats enregistres partagent la meme surface. Limitez Claude Code aux tokens existants et faites-le s’arreter s’il manque un token.
Troisieme cas : le CSS de contenu CMS. Markdown et MDX ont souvent des regles globales sur h2, table, pre et blockquote. Limitez les modifications a .legacy-markdown et comparez quelques pages publiees.
Quatrieme cas : demarrer un design system. Ne remplacez pas tout. Commencez par les cartes et boutons, car le diff reste petit et facile a valider.
Pieges a verifier
Le premier piege est !important. S’il semble necessaire, Claude Code doit expliquer le conflit de selecteurs. Le deuxieme est un mode sombre sans contraste. Le troisieme est de tout baser sur le viewport alors que le probleme depend du conteneur. Le quatrieme est un test visuel instable a cause des polices externes, animations, donnees aleatoires ou publicites dynamiques.
Regression visuelle avec Playwright
Ne laissez pas toute la verification aux yeux humains. Playwright peut comparer la carte a plusieurs tailles, en mode clair, en mode sombre et avec le focus clavier visible.
// tests/visual/style-regression.spec.ts
import { expect, test } from "@playwright/test";
const viewports = [
{ name: "mobile", width: 375, height: 812 },
{ name: "tablet", width: 768, height: 1024 },
{ name: "desktop", width: 1440, height: 900 },
];
for (const viewport of viewports) {
test(`pricing card visual regression ${viewport.name}`, async ({ page }) => {
await page.setViewportSize({ width: viewport.width, height: viewport.height });
await page.emulateMedia({ colorScheme: "light", reducedMotion: "reduce" });
await page.goto("/style-lab");
const card = page.getByTestId("pricing-card").first();
await expect(card).toBeVisible();
await expect(card).toHaveScreenshot(`pricing-card-${viewport.name}-light.png`, {
animations: "disabled",
maxDiffPixelRatio: 0.02,
});
});
}
test("dark theme keeps focus states visible", async ({ page }) => {
await page.emulateMedia({ colorScheme: "dark", reducedMotion: "reduce" });
await page.goto("/style-lab");
await page.locator("html").evaluate((element) => {
element.setAttribute("data-theme", "dark");
});
const startButton = page.getByRole("link", { name: /start with training/i });
await startButton.focus();
await expect(startButton).toBeFocused();
await expect(page.getByTestId("pricing-card").first()).toHaveScreenshot(
"pricing-card-dark-focus.png",
{
animations: "disabled",
maxDiffPixelRatio: 0.02,
},
);
});
npx playwright test tests/visual/style-regression.spec.ts --update-snapshots
npx playwright test tests/visual/style-regression.spec.ts
Terminez par une revue critique :
Critically review the CSS diff.
Check cascade layers, token usage, selector specificity, dark mode,
container queries, keyboard focus, color contrast, reduced motion,
and Playwright visual coverage.
Return only concrete issues with file paths and line numbers.
CTA et retour terrain
Un article CSS doit proposer une suite logique. Les developpeurs individuels peuvent partir de la checklist gratuite. Les equipes peuvent passer a la formation et consultation Claude Code. Pour rendre les regles repetables, lisez harness engineering.
Lors de mon essai, le plus grand gain ne venait pas de @layer seul, mais du prompt d’inspection initial. Quand Claude Code lisait d’abord les styles, tokens et commandes de validation, le diff restait limite a la carte et au bouton. Avec un simple “ameliore le style”, il ajoutait des couleurs en dur, des espacements dupliques et aucun check visuel.
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.