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.
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.
Artigos relacionados
Workflow Obsidian para CLAUDE.md com Claude Code
Transforme notas de trabalho do Obsidian em notas operacionais CLAUDE.md para preservar contexto.
Claude Code Revenue CTA Routing: artigos para PDF, Gumroad e consultoria
Um fluxo com Claude Code para levar leitores ao PDF grátis, Gumroad ou consultoria conforme intenção.
Regras de handoff para equipes com Claude Code: evidências, permissões, rollback e receita
Formato prático para entregar trabalho do Claude Code com prova, permissões, rollback, PDF grátis, Gumroad e consultoria.