Design Tokens con Claude Code: de Figma a variables CSS, Tailwind y React
Implementa design tokens con Claude Code, Style Dictionary, variables CSS, Tailwind y React.
Los design tokens son un contrato para cambiar la UI
Los design tokens guardan decisiones de diseño como datos con nombre: colores, espaciado, tipografía, radios, sombras y estados. En lugar de repetir #2563eb, 16px o 0.5rem dentro de muchos componentes, defines esos valores en tokens.json y generas salidas para CSS, Tailwind o React.
Claude Code encaja muy bien en este flujo porque puede leer CSS existente, detectar valores duplicados, proponer nombres semánticos, actualizar componentes, ejecutar la generación y resumir el diff. El prompt peligroso es “haz que la UI sea consistente”. El prompt útil indica el archivo fuente, los componentes incluidos, las reglas de contraste y los comandos que deben pasar.
Para ampliar el tema, revisa diseño de sistemas con Claude Code y accesibilidad con Claude Code. Las referencias oficiales son Design Tokens Format Module 2025.10, Claude Code docs, Figma Variables plugin API, Tailwind theme docs, Style Dictionary, MDN CSS custom properties y WCAG sobre contraste.
Raw tokens y semantic tokens
Un raw token describe el material: color.blue.600, space.4, font.size.base. Un semantic token describe el propósito: color.action.primary.bg, color.text.muted, color.surface.default. Un component token describe una regla propia de un componente, como button.primary.paddingX.
| Tipo | Ejemplo | Uso recomendado |
|---|---|---|
| Raw | color.blue.600 | Paletas, escalas de espacio y tipografía |
| Semantic | color.action.primary.bg | Significado de producto, temas y modo oscuro |
| Component | button.primary.paddingX | Excepciones estables de un componente |
Empieza con raw y semantic. Si un botón usa color.blue.600, un rebranding puede dejar nombres engañosos. Si usa color.action.primary.bg, el valor cambia sin romper el significado.
Convertir una Figma-like spec en contrato de código
Figma Variables ayuda mucho, pero Claude Code necesita un handoff revisable, no un volcado de nombres. Una Figma-like spec declara archivo de origen, modos, cambio solicitado, componentes afectados y reglas de revisión. Así reduces el risk de copiar Blue / 600 en React cuando el producto necesita 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."
]
}
Pide a Claude Code una tabla de impacto antes de editar. Este workflow evita un pitfall frecuente: un solo cambio de token puede tocar fondos de botones, color de enlaces, acentos de tarjetas y focus outline.
tokens.json ejecutable
Guarda esto 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)" }
}
}
Generar variables CSS con 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"
}
}
CSS generado:
: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 y 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;
}
Casos reales, errores y revisión
Tres usos prácticos son claros. En el handoff de Figma, Claude Code puede comparar el export con tokens.json y listar altas, bajas, renombres e impacto en componentes. Para modo oscuro, cambia tokens semánticos como surface, text y action sin duplicar CSS de componentes. En un rebranding o una landing page, la generación de tokens evita que colores y espacios se desvíen página por página.
Los errores frecuentes son crear demasiados component tokens desde el primer día, nombrar por apariencia (blueButton) en vez de intención (primaryAction), editar a mano tokens.css generado y revisar contraste demasiado tarde. Para texto normal, WCAG AA pide al menos 4.5:1; valida estado normal, hover, disabled, focus, tema claro y tema oscuro.
Review checklist:
-
tokens/tokens.jsones la única fuente editada; el CSS generado no se tocó a mano. - Button, Link y Card se revisaron en light, dark, hover, disabled y focus.
- React y Tailwind consumen semantic tokens, no raw palette tokens.
- El PR explica Figma-like spec, use case, pitfall, risk y workflow.
- El contraste WCAG 2.2 AA se validó para texto e interacciones.
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.
Cierre
Un flujo práctico es mantener tokens.json, generar variables CSS con Style Dictionary, mapearlas en Tailwind, consumir semantic tokens en componentes y revisar drift y contraste en cada PR. Probé el recorrido con el token file de ejemplo y un Button que consume color, espaciado, radius, shadow y focus outline. Para prompts y checklists reutilizables, consulta productos de ClaudeCodeLab. En producción añade Storybook, axe y revisión visual; para acompañamiento de equipo, usa formación y consultoría de ClaudeCodeLab.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Workflow de Obsidian a CLAUDE.md con Claude Code
Convierte notas de trabajo de Obsidian en notas operativas de CLAUDE.md para no repetir contexto.
Claude Code Revenue CTA Routing: de artículos a PDF, Gumroad y consulta
Un flujo con Claude Code para dirigir lectores a PDF gratis, Gumroad o consulta según intención.
Reglas de handoff para equipos con Claude Code: evidencia, permisos, rollback e ingresos
Formato práctico para entregar trabajo de Claude Code con pruebas, permisos, rollback, PDF gratis, Gumroad y consulta.