Implementá un design token escalable con Tailwind y CSS vars
Tutorial completo: llevá tus tokens de Figma a producción con Tailwind y CSS custom properties. Sincronización automatizada, escalable y sin fricción.
Equipo NUCBA
Implementá un design token escalable con Tailwind y CSS vars
El problema no es Tailwind. El problema es que tu tailwind.config.js tiene 47 sombras hardcodeadas, 12 espaciados inventados sobre la marcha y colores con nombres como bluish-gray-kinda-dark. Cuando diseño cambia un color primario, tocás 23 archivos. Cuando un dev nuevo llega, usa #3B82F6 directo en el HTML porque no sabe que ese azul "ya existe" como primary-600.
Un sistema de design tokens bien implementado resuelve esto: una única fuente de verdad que va de Figma a producción, sin fricción. Este tutorial te muestra cómo configurar tokens con CSS custom properties y Tailwind para que escalen, se mantengan solos y tu equipo deje de inventar valores.
De Figma a CSS: exportá tus tokens como variables
Figma tiene los tokens. Tu código necesita consumirlos. El puente son las CSS custom properties.
Paso 1: Organizá tus tokens en Figma
Antes de exportar nada, tu archivo de diseño necesita estructura. Creá variables en Figma (no estilos, variables) organizadas por categorías:
- Primitivos (base): colores puros, espaciados base, tipografía raw
- Semánticos (contexto):
color/text/primary,spacing/section/gap,font/heading/large - Componentes (específicos):
button/padding,card/radius
Ejemplo real de estructura:
color/gray/50: #FAFAFA
color/gray/900: #111827
color/brand/primary: #6366F1
color/semantic/text-primary: {color/gray/900}
color/semantic/text-inverse: {color/gray/50}
Los semánticos referencian primitivos. Esto te permite cambiar un valor base y que se propague.
Paso 2: Exportá como CSS custom properties
Usá un plugin como Figma Tokens Studio o Design Tokens para exportar. Si tu equipo no tiene plugin, hacelo manual una vez y automatizalo después.
Output esperado en tokens.css:
:root { /* Primitivos */ --color-gray-50: #FAFAFA; --color-gray-900: #111827; --color-brand-primary: #6366F1; /* Semánticos */ --color-text-primary: var(--color-gray-900); --color-text-inverse: var(--color-gray-50); /* Espaciados */ --spacing-1: 0.25rem; --spacing-4: 1rem; --spacing-section: 4rem; /* Tipografía */ --font-size-base: 1rem; --font-size-lg: 1.125rem; --line-height-tight: 1.25; }
Paso 3: Importá el archivo en tu proyecto
En tu entrada principal (generalmente index.css o global.css):
@import './tokens.css'; @tailwind base; @tailwind components; @tailwind utilities;
Las CSS vars ahora están disponibles en todo tu proyecto. Pero Tailwind todavía no sabe que existen.
Configurá Tailwind para consumir tus CSS vars
Acá es donde la mayoría se traba. Tailwind tiene su propio sistema de colores y espaciados. No querés reemplazarlo, querés extenderlo con tus tokens.
Extendé el theme en tailwind.config.js
/** @type {import('tailwindcss').Config} */ module.exports = { content: ['./src/**/*.{js,jsx,ts,tsx}'], theme: { extend: { colors: { 'text-primary': 'var(--color-text-primary)', 'text-inverse': 'var(--color-text-inverse)', brand: { primary: 'var(--color-brand-primary)', }, gray: { 50: 'var(--color-gray-50)', 900: 'var(--color-gray-900)', }, }, spacing: { 'section': 'var(--spacing-section)', }, fontSize: { 'base': 'var(--font-size-base)', 'lg': ['var(--font-size-lg)', { lineHeight: 'var(--line-height-tight)' }], }, }, }, plugins: [], }
Ahora podés usar text-text-primary o bg-brand-primary en tu HTML. Tailwind genera las clases que apuntan a tus CSS vars.
Beneficio clave: cambiás el valor de --color-brand-primary en un solo lugar y todas las clases se actualizan. Incluso podés implementar tema oscuro sobrescribiendo variables:
[data-theme="dark"] { --color-text-primary: var(--color-gray-50); --color-text-inverse: var(--color-gray-900); }
Cero cambios en tu HTML. Cero nuevas clases.
Automatizá la sincronización con un pipeline
Mantener tokens.css y tailwind.config.js en sync manual no escala. Necesitás un proceso que genere código a partir de tus tokens.
Opción 1: Style Dictionary (el estándar)
Style Dictionary de Amazon es la herramienta más usada. Tomá tus tokens de Figma en JSON y transformalos a CSS vars + configuración Tailwind.
Instalá:
npm install style-dictionary
Creá tokens.json (exportado de Figma):
{ "color": { "brand": { "primary": { "value": "#6366F1" } }, "text": { "primary": { "value": "{color.gray.900}" } } } }
Configurá config.js:
module.exports = { source: ['tokens.json'], platforms: { css: { transformGroup: 'css', buildPath: 'src/styles/', files: [{ destination: 'tokens.css', format: 'css/variables' }] } } }
Correlo:
npx style-dictionary build
Genera automáticamente tokens.css. Para Tailwind, necesitás un formato custom o un script que lea el JSON y genere el objeto theme.extend.
Opción 2: Plugin de Figma a código
Plugins como Figma Tokens Studio pueden pushear cambios directamente a tu repo vía GitHub Actions. Cada vez que diseño actualiza tokens en Figma, se dispara un PR con los cambios en código.
Workflow básico:
- Diseño actualiza variables en Figma
- Plugin exporta JSON a rama
design-tokens - GitHub Action corre Style Dictionary
- Abre PR con
tokens.cssy config actualizada - Dev revisa, mergea
Tu código siempre está en sync con diseño. Sin Slack, sin "che, ¿cuál era el spacing del botón?".
Errores comunes que rompen la escalabilidad
1. Hardcodear valores "porque es más rápido"
// ❌ Mal <div className="text-[#6366F1]"> // ✅ Bien <div className="text-brand-primary">
Un valor hardcodeado se convierte en 50 antes de que te des cuenta. Establecé un linter que bloquee hex/rgb directo en clases.
2. Crear tokens demasiado específicos
/* ❌ Mal: token que solo se usa en un lugar */ --button-primary-hover-shadow-color: rgba(99, 102, 241, 0.3); /* ✅ Bien: token reutilizable */ --shadow-color-brand: rgba(99, 102, 241, 0.3);
Si un token tiene un solo caso de uso, probablemente sea ruido. Mantené tokens que se reusen al menos 3 veces.
3. Ignorar el modo oscuro desde el inicio
Implementar dark mode después es refactorizar todo. Planealo desde el día 1 usando tokens semánticos:
:root { --bg-primary: white; --text-primary: black; } [data-theme="dark"] { --bg-primary: black; --text-primary: white; }
Tus componentes usan bg-bg-primary, el tema solo cambia el valor de la variable.
4. No documentar la convención de nombres
Tu equipo necesita saber cuándo usar color-brand-primary vs color-text-primary. Documentá:
- Primitivos: valores raw, no usar directamente en componentes
- Semánticos: valores contextuales, usar en la mayoría de casos
- Componentes: valores específicos de un componente, usar solo ahí
Checklist: tu sistema está listo para producción
Antes de mergear, verificá:
- Todos los colores en uso tienen un token correspondiente
-
tailwind.config.jsextiende (no reemplaza) valores default - Existe un
tokens.csscon todas las CSS vars organizadas - Hay al menos una distinción entre tokens primitivos y semánticos
- El pipeline de build no rompe si cambia un token
- Existe documentación de convención de nombres
- Dark mode está implementado o planeado con estructura de variables
Preguntas frecuentes
¿Puedo usar tokens con Tailwind sin CSS custom properties?
Sí, podés definir todo directo en tailwind.config.js. Pero perdés la capacidad de cambiar valores en runtime (dark mode dinámico, theming por cliente) y JS es menos flexible para sincronizar con Figma. CSS vars te dan más opciones.
¿Cómo manejo tokens que Tailwind no soporta nativamente, como border-radius complejos?
Extendé cualquier propiedad en theme.extend. Para casos muy custom, usá @layer components en CSS y aplicá variables ahí:
@layer components { .card-custom { border-radius: var(--radius-card); box-shadow: var(--shadow-elevated); } }
¿Qué pasa si diseño y código se desincronicen?
Automatización. Si tu pipeline está bien configurado, cada cambio en Figma dispara una actualización en código. Si no tenés pipeline, establecé una revisión semanal donde diseño y frontend sincronizan tokens manualmente. Sin proceso, los tokens mueren.