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
Checklist de permisos antes de que Claude Code edite un sitio de cliente
Guía para agencias que quieren usar IA en landing pages sin tocar zonas sensibles.
Convierte tickets de soporte SaaS en pasos reproducibles con Claude Code
Flujo para transformar reportes vagos en pasos, evidencia y una nota útil para ingeniería.
Convierte tus notas viejas de Obsidian en instrucciones para Claude Code en 10 minutos
Rutina de 10 minutos para separar tus notas de Obsidian en hechos, decisiones y dudas, y darle a Claude Code instrucciones que sí funcionan.