Design tokens avec Claude Code : de Figma aux variables CSS, Tailwind et React
Implémentez des design tokens avec Claude Code, Style Dictionary, variables CSS, Tailwind et React.
Les design tokens sont un contrat de changement UI
Les design tokens stockent les décisions de design sous forme de données nommées : couleurs, espacements, typographie, rayons, ombres et états. Au lieu de répéter #2563eb, 16px ou 0.5rem dans plusieurs composants, on place la source de vérité dans tokens.json, puis on génère des variables CSS, une configuration Tailwind et du code utilisable par les composants.
Claude Code est utile parce que ce travail est vérifiable. Il peut lire le CSS existant, repérer les valeurs codées en dur, proposer des noms sémantiques, modifier les composants, lancer la génération et résumer le diff. Le prompt vague “rends l’interface cohérente” produit souvent trop de changements. Un bon prompt précise les fichiers, les composants concernés, les règles de contraste et les commandes à exécuter.
Pour compléter, consultez le guide design system avec Claude Code et l’accessibilité avec Claude Code. Les références officielles à garder sous la main sont Design Tokens Format Module 2025.10, Claude Code docs, Figma Variables plugin API, Tailwind theme docs, Style Dictionary, MDN sur les propriétés personnalisées CSS et WCAG sur le contraste.
Raw tokens et semantic tokens
Un raw token décrit la matière première : color.blue.600, space.4, font.size.base. Un semantic token décrit l’intention : color.action.primary.bg, color.text.muted, color.surface.default. Un component token décrit une règle locale, par exemple button.primary.paddingX.
| Type | Exemple | Utilisation |
|---|---|---|
| Raw | color.blue.600 | Palette, échelle d’espacement, typographie |
| Semantic | color.action.primary.bg | Sens produit, thème, mode sombre |
| Component | button.primary.paddingX | Exception stable d’un composant |
Commencez avec raw et semantic. Si un bouton consomme directement color.blue.600, un changement de marque peut laisser un nom faux. Avec color.action.primary.bg, la valeur peut changer sans perdre l’intention.
Transformer une Figma-like spec en contrat de code
Figma Variables est utile, mais Claude Code doit recevoir un handoff vérifiable, pas un export de noms brut. Une Figma-like spec précise le fichier source, les modes, le changement demandé, les composants touchés et les règles de revue. Cela réduit le risk de copier Blue / 600 dans React alors que le produit a besoin de color.action.primary.bg.
{
"figmaSource": {
"file": "Marketing UI Kit",
"collection": "Brand v2",
"modes": ["light", "dark"]
},
"changeRequest": {
"type": "replace-token",
"from": "color.blue.600",
"to": "color.action.primary.bg",
"components": ["Button", "Link", "Card"]
},
"reviewRules": [
"Do not use raw color tokens in component CSS.",
"Keep focus and hover tokens explicit.",
"List affected use case and pitfall before editing files."
]
}
Demandez d’abord à Claude Code une table d’impact. Ce workflow évite un pitfall courant : un seul token peut modifier les boutons, liens, cartes et focus outlines.
Un tokens.json exécutable
Créez tokens/tokens.json :
{
"color": {
"blue": {
"50": { "$type": "color", "$value": "#eff6ff" },
"600": { "$type": "color", "$value": "#2563eb" },
"700": { "$type": "color", "$value": "#1d4ed8" }
},
"slate": {
"50": { "$type": "color", "$value": "#f8fafc" },
"100": { "$type": "color", "$value": "#f1f5f9" },
"700": { "$type": "color", "$value": "#334155" },
"900": { "$type": "color", "$value": "#0f172a" }
},
"white": { "$type": "color", "$value": "#ffffff" },
"focus": { "$type": "color", "$value": "#f59e0b" },
"surface": {
"default": { "$type": "color", "$value": "{color.white}" },
"muted": { "$type": "color", "$value": "{color.slate.50}" },
"inverse": { "$type": "color", "$value": "{color.slate.900}" }
},
"text": {
"default": { "$type": "color", "$value": "{color.slate.900}" },
"muted": { "$type": "color", "$value": "{color.slate.700}" },
"inverse": { "$type": "color", "$value": "{color.white}" }
},
"action": {
"primary": {
"bg": { "$type": "color", "$value": "{color.blue.600}" },
"bgHover": { "$type": "color", "$value": "{color.blue.700}" },
"text": { "$type": "color", "$value": "{color.white}" }
}
}
},
"dark": {
"color": {
"surface": {
"default": { "$type": "color", "$value": "{color.slate.900}" },
"muted": { "$type": "color", "$value": "{color.slate.700}" }
},
"text": {
"default": { "$type": "color", "$value": "{color.white}" },
"muted": { "$type": "color", "$value": "{color.slate.100}" }
},
"action": {
"primary": {
"bg": { "$type": "color", "$value": "{color.blue.50}" },
"bgHover": { "$type": "color", "$value": "{color.white}" },
"text": { "$type": "color", "$value": "{color.slate.900}" }
}
}
}
},
"space": {
"2": { "$type": "dimension", "$value": "0.5rem" },
"3": { "$type": "dimension", "$value": "0.75rem" },
"4": { "$type": "dimension", "$value": "1rem" },
"6": { "$type": "dimension", "$value": "1.5rem" }
},
"font": {
"size": {
"sm": { "$type": "dimension", "$value": "0.875rem" },
"base": { "$type": "dimension", "$value": "1rem" },
"lg": { "$type": "dimension", "$value": "1.125rem" }
},
"weight": {
"medium": { "$type": "fontWeight", "$value": "500" },
"bold": { "$type": "fontWeight", "$value": "700" }
}
},
"radius": {
"md": { "$type": "dimension", "$value": "0.5rem" },
"lg": { "$type": "dimension", "$value": "0.75rem" }
},
"shadow": {
"button": { "$type": "shadow", "$value": "0 1px 2px rgb(15 23 42 / 0.16)" }
}
}
Générer les variables CSS
npm install --save-dev style-dictionary
style-dictionary.config.js :
export default {
source: ["tokens/tokens.json"],
hooks: {
formats: {
"css/variables-with-dark": ({ dictionary }) => {
const light = dictionary.allTokens
.filter((token) => !token.path.includes("dark"))
.map((token) => ` --${token.name}: ${token.value};`)
.join("\n");
const dark = dictionary.allTokens
.filter((token) => token.path[0] === "dark")
.map((token) => ` --${token.path.slice(1).join("-")}: ${token.value};`)
.join("\n");
return `:root {\n${light}\n}\n\n[data-theme="dark"] {\n${dark}\n}\n`;
}
}
},
platforms: {
css: {
transformGroup: "css",
buildPath: "src/styles/",
files: [{ destination: "tokens.css", format: "css/variables-with-dark" }]
}
}
};
Ajoutez le script :
{
"scripts": {
"tokens:build": "style-dictionary build --config style-dictionary.config.js"
}
}
Sortie CSS attendue :
:root {
--color-action-primary-bg: #2563eb;
--color-action-primary-bg-hover: #1d4ed8;
--color-action-primary-text: #ffffff;
--space-3: 0.75rem;
--space-4: 1rem;
--font-size-base: 1rem;
--font-weight-bold: 700;
--radius-md: 0.5rem;
--shadow-button: 0 1px 2px rgb(15 23 42 / 0.16);
}
[data-theme="dark"] {
--color-surface-default: #0f172a;
--color-text-default: #ffffff;
--color-action-primary-bg: #eff6ff;
--color-action-primary-text: #0f172a;
}
Tailwind et Button React
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{ts,tsx,js,jsx,mdx}"],
theme: {
extend: {
colors: {
surface: { DEFAULT: "var(--color-surface-default)", muted: "var(--color-surface-muted)" },
text: { DEFAULT: "var(--color-text-default)", muted: "var(--color-text-muted)", inverse: "var(--color-text-inverse)" },
action: { primary: "var(--color-action-primary-bg)", "primary-hover": "var(--color-action-primary-bg-hover)" }
},
spacing: { 3: "var(--space-3)", 4: "var(--space-4)", 6: "var(--space-6)" },
borderRadius: { md: "var(--radius-md)", lg: "var(--radius-lg)" },
boxShadow: { button: "var(--shadow-button)" }
}
}
};
import "./Button.css";
type ButtonProps = {
children: React.ReactNode;
onClick?: () => void;
disabled?: boolean;
};
export function Button({ children, onClick, disabled = false }: ButtonProps) {
return (
<button className="Button" onClick={onClick} disabled={disabled}>
{children}
</button>
);
}
.Button {
background: var(--color-action-primary-bg);
border: 0;
border-radius: var(--radius-md);
box-shadow: var(--shadow-button);
color: var(--color-action-primary-text);
cursor: pointer;
font-size: var(--font-size-base);
font-weight: var(--font-weight-bold);
padding: var(--space-3) var(--space-4);
}
.Button:hover:not(:disabled) {
background: var(--color-action-primary-bg-hover);
}
.Button:focus-visible {
outline: 3px solid var(--color-focus);
outline-offset: 2px;
}
.Button:disabled {
cursor: not-allowed;
opacity: 0.55;
}
Cas d’usage, pièges et revue
Trois cas reviennent souvent. Pour le passage Figma vers code, Claude Code compare l’export avec tokens.json et liste ajouts, suppressions, renommages et composants touchés. Pour le mode sombre, on surcharge les tokens sémantiques surface, text et action au lieu de dupliquer les feuilles de style. Pour un rebranding ou une landing page, la génération évite la dérive des couleurs et des espacements.
Les pièges concrets : créer trop de component tokens dès le départ, nommer par apparence (blueButton) au lieu de l’intention (primaryAction), modifier à la main le fichier tokens.css généré, ou vérifier le contraste trop tard. Pour du texte normal, WCAG AA demande au moins 4,5:1.
Review checklist :
-
tokens/tokens.jsonest la seule source modifiée ; le CSS généré n’a pas été retouché à la main. - Button, Link et Card ont été vérifiés en light, dark, hover, disabled et focus.
- React et Tailwind consomment des semantic tokens, pas des raw palette tokens.
- La PR explique Figma-like spec, use case, pitfall, risk et workflow.
- Le contraste WCAG 2.2 AA a été vérifié pour le texte et les états interactifs.
Design token review task:
- Read tokens/tokens.json, style-dictionary.config.js, src/styles/tokens.css, tailwind.config.js, and src/components/Button.tsx.
- Check that components use semantic tokens, not raw color tokens.
- Verify light and dark theme values for button, surface, and text tokens.
- Flag any generated CSS file that was edited manually.
- Check WCAG 2.2 AA contrast for normal text and button text.
- Suggest the smallest safe diff. Do not rename tokens unless you list every affected component.
- After changes, run npm run tokens:build and the focused component tests.
Conclusion
Un flux solide consiste à maintenir tokens.json, générer les variables CSS avec Style Dictionary, les exposer dans Tailwind, consommer seulement des semantic tokens dans les composants et contrôler drift et contraste dans chaque PR. J’ai vérifié le parcours de l’exemple en générant les variables CSS puis en les branchant sur un Button React. Pour des prompts et checklists réutilisables, consultez les produits ClaudeCodeLab. En production, ajoutez Storybook, axe et une revue visuelle ; pour un déploiement en équipe, consultez la formation et le conseil ClaudeCodeLab.
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
Workflow Obsidian vers CLAUDE.md avec Claude Code
Transformer des notes Obsidian en notes CLAUDE.md concises pour reprendre les sessions sans réexpliquer.
Claude Code Revenue CTA Routing : relier articles, PDF, Gumroad et consultation
Un workflow Claude Code pour orienter les lecteurs vers PDF gratuit, Gumroad ou consultation selon l'intention.
Règles de handoff Claude Code en équipe: preuves, permissions, rollback et revenus
Un format concret pour transmettre un travail Claude Code avec preuves, permissions, rollback, PDF gratuit, Gumroad et consultation.