Design Tokens com Claude Code: do Figma a variáveis CSS, Tailwind e React
Implemente design tokens com Claude Code, Style Dictionary, variáveis CSS, Tailwind e React.
Design tokens são um contrato para mudar a UI
Design tokens guardam decisões de design como dados nomeados: cores, espaçamento, tipografia, raios, sombras e estados. Em vez de espalhar #2563eb, 16px e 0.5rem por vários componentes, você mantém a fonte da verdade em tokens.json e gera variáveis CSS, configuração do Tailwind e valores consumidos por React.
Claude Code funciona bem nesse fluxo porque o trabalho é revisável. Ele pode ler o CSS atual, encontrar valores hardcoded, sugerir nomes semânticos, atualizar componentes, rodar o build e resumir o diff. O pedido “deixe a UI consistente” é amplo demais. Um pedido melhor define o arquivo de tokens, os componentes no escopo, as regras de contraste e os comandos que precisam passar.
Para continuar, veja design system com Claude Code e acessibilidade com Claude Code. Use como referência oficial o Design Tokens Format Module 2025.10, Claude Code docs, Figma Variables plugin API, Tailwind theme docs, a documentação do Style Dictionary, MDN CSS custom properties e WCAG sobre contraste.
Raw tokens e semantic tokens
Raw tokens descrevem a matéria-prima: color.blue.600, space.4, font.size.base. Semantic tokens descrevem a intenção: color.action.primary.bg, color.text.muted, color.surface.default. Component tokens descrevem regras locais, como button.primary.paddingX.
| Tipo | Exemplo | Uso |
|---|---|---|
| Raw | color.blue.600 | Paletas, escalas de espaço e tipografia |
| Semantic | color.action.primary.bg | Sentido de produto, temas e dark mode |
| Component | button.primary.paddingX | Exceções estáveis de componentes |
Comece com raw e semantic. Se um botão usa color.blue.600 diretamente, uma mudança de marca pode deixar nomes errados. Se ele usa color.action.primary.bg, o valor muda sem perder o significado.
Transformar uma Figma-like spec em contrato de código
Figma Variables ajudam, mas Claude Code deve receber um handoff revisável, não um export bruto de nomes. Uma Figma-like spec declara arquivo de origem, modos, mudança pedida, componentes afetados e regras de review. Isso reduz o risk de copiar Blue / 600 para React quando o produto precisa 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."
]
}
Peça primeiro uma tabela de impacto e só depois autorize edição. Esse workflow evita um pitfall comum: um token pode mexer ao mesmo tempo em Button, Link, Card e focus outline.
tokens.json executável
Salve como 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)" }
}
}
Gerar CSS com Style Dictionary
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" }]
}
}
};
Script:
{
"scripts": {
"tokens:build": "style-dictionary build --config style-dictionary.config.js"
}
}
Saída esperada:
: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 e React Button
/** @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;
}
Casos de uso, armadilhas e revisão
Três casos aparecem com frequência. No handoff do Figma para código, Claude Code compara o export com tokens.json e lista valores adicionados, removidos, renomeados e componentes afetados. No dark mode, você sobrescreve tokens semânticos como surface, text e action, sem duplicar CSS. Em rebranding ou landing pages, o build de tokens e screenshots no Storybook evitam drift visual.
Armadilhas concretas: criar muitos component tokens no primeiro dia, nomear pela aparência (blueButton) em vez da intenção (primaryAction), editar manualmente o tokens.css gerado e deixar contraste para o fim. Para texto normal, WCAG AA exige pelo menos 4.5:1.
Review checklist:
-
tokens/tokens.jsoné a única fonte editada; o CSS gerado não foi alterado manualmente. - Button, Link e Card foram revisados em light, dark, hover, disabled e focus.
- React e Tailwind consomem semantic tokens, não raw palette tokens.
- O PR explica Figma-like spec, use case, pitfall, risk e workflow.
- O contraste WCAG 2.2 AA foi validado para texto e estados interativos.
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.
Fechamento
O fluxo prático é manter tokens.json, gerar variáveis CSS com Style Dictionary, mapear no Tailwind, consumir semantic tokens nos componentes e revisar drift e contraste em cada PR. Testei o caminho do exemplo gerando CSS variables e conectando um Button React a cor, espaçamento, radius, shadow e focus outline. Para prompts e checklists reutilizáveis, veja produtos ClaudeCodeLab. Em produção, acrescente Storybook, axe e revisão visual; para adoção em equipe, veja treinamento e consultoria ClaudeCodeLab.
Artigos relacionados
Como criar um design system com Claude Code: Design Tokens, Storybook e CI
Adote Design Tokens, React, Storybook, acessibilidade e CI com Claude Code em um fluxo seguro e verificável.
CSS de producao com Claude Code: camadas, tokens e regressao visual
Use Claude Code para organizar CSS de producao com camadas, tokens, container queries, modo escuro e testes visuais.
Variaveis CSS com Claude Code: tokens de tema praticos
Use Claude Code para implementar variaveis CSS, var(), tokens de tema, modo escuro e revisoes de UI.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.