Diseño de interfaces

Crear tokens de color escalables para sistemas de diseño

Por el equipo de colorPaletteFinder9 min de lectura

El primer sistema de diseño que construí salió con exactamente un error que persiguió a todo lo demás después: usábamos blue-500 directamente en el código de los componentes. Los botones eran blue-500. Los enlaces eran blue-500. El anillo de foco era blue-500. Se sentía limpio y DRY. Luego marketing rehízo la marca de azul a verde azulado, y nos pasamos dos sprints persiguiendo cada referencia hex literal a lo largo de cuarenta repositorios, y aun así se nos escaparon unas cuantas, porque algunas se habían codificado como #3B82F6 en lugar de la variable. Todo ese episodio podría haber sido un cambio de una sola línea. La razón por la que no lo fue es la razón entera por la que existen los tokens de color de sistema de diseño.

Un token de color no es más que una referencia con nombre a un valor de color. Pero el valor de los tokens no está en el nombre: está en las capas de nombres, y en ser disciplinado sobre qué capa tiene permitido tocar un componente. Acierta con la estratificación y un rebranding, un lanzamiento de modo oscuro o una corrección de contraste se convierten en un cambio pequeño y contenido. Falla y obtienes la limpieza de cuarenta repositorios.

Tres capas: primitiva, semántica, de componente

El modelo mental que ha aguantado en cada sistema en el que he trabajado es una jerarquía de tres niveles. Cada nivel referencia al de debajo, y los componentes solo tienen permitido consumir el nivel superior.

Aquí está la regla que hace que todo funcione, y la que la mayoría de los equipos se saltan: los componentes referencian tokens semánticos o de componente, nunca primitivos. En el momento en que un botón se salta la capa semántica para agarrar blue-500 directamente, has reintroducido el problema de los cuarenta repositorios, un componente cada vez.

¿Por qué molestarse en absoluto con la capa semántica intermedia? Porque es la costura donde ocurre el theming. Cuando cambias a modo oscuro, no repintas cada componente: reapuntas unas pocas docenas de tokens semánticos a primitivos distintos. Los componentes no se enteran de que algo ha cambiado. Esa indirección es la decisión de mayor apalancamiento de todo el sistema.

Construye la escala antes de nombrar nada

Antes de la semántica, necesitas primitivos a los que valga la pena apuntar, y eso significa una escala numérica, convencionalmente del 50 al 950. La convención popularizada por Tailwind (50, 100, 200 … 900, 950) se ha convertido de hecho en el estándar de la industria, y vale la pena adoptarla aunque solo sea para que los nuevos ingenieros se sientan en casa. 50 es el tinte más claro, 500 es aproximadamente el tono de marca puro, 950 es casi negro.

El error que cometen los principiantes es generar una escala aclarando y oscureciendo ingenuamente en sRGB: añadiendo blanco para los tintes, negro para las sombras. Obtienes medios tonos enturbiados y pasos que no se sienten espaciados de forma uniforme, porque la luminosidad percibida no es lineal en RGB. Un 400 acaba pareciendo casi idéntico a un 500, mientras que de 700 a 800 hay un precipicio. La solución es espaciar tus pasos en un modelo perceptual. Las herramientas que trabajan en HSL te llevan la mayor parte del camino; las que usan OKLCH (ahora bien soportado en CSS) te llevan el resto, porque OKLCH mantiene la luminosidad percibida consistente a medida que cambia el tono. Si quieres juzgar a ojo tintes y sombras armoniosos rápidamente mientras prototipas una escala, el generador de paletas de color es una forma veloz de ver las relaciones entre tonos antes de comprometerlos en tokens.

Unas cuantas reglas ganadas a pulso para las escalas:

Nomenclatura: describe el trabajo, no la apariencia

El principio de nomenclatura más importante es este: los tokens semánticos y de componente deben nombrar el propósito, nunca la apariencia. Un token llamado color-text-red se rompe el día en que el texto de error pasa a ser naranja: el nombre ahora miente. Un token llamado color-feedback-error sobrevive a ese cambio intacto, porque "error" sigue siendo cierto independientemente del rojo (o naranja) al que resuelva.

Una estructura de nomenclatura que escala se lee de la categoría general al modificador específico, porque ordena y audita de forma limpia:

Ese último —color-text-on-action— es un token que la gente olvida y lamenta. Cuando tu color de acción es un azul saturado, el texto sobre ese azul necesita ser blanco o casi blanco, y ese emparejamiento no tiene nada que ver con color-text-primary. Conviértelo en un token explícito o verás a los ingenieros codificar a fuego #FFFFFF sobre los botones y luego abrir bugs de accesibilidad cuando alguien introduzca una acción de "advertencia" amarillo claro.

Para los primitivos, nombrar la apariencia es correcto y esperado —blue-500 debería describir lo que es, porque ese es todo el sentido de la capa primitiva—. La regla de "describir la intención" se aplica por encima de ella.

Implementación con propiedades personalizadas CSS

Las propiedades personalizadas CSS son el hogar natural de los tokens porque cascadean y pueden ser sobrescritas por el contexto, que es exactamente cómo funciona el theming. El patrón son dos capas de variables: los primitivos definidos una vez en la raíz, y los semánticos definidos en la raíz y redefinidos bajo un selector de tema.

Define los primitivos globalmente: --blue-600: #2563EB, --gray-900: #111827, y así sucesivamente. Luego mapea los semánticos a ellos: --color-action-primary: var(--blue-600) y --color-text-primary: var(--gray-900). Los componentes solo leen las variables semánticas: color: var(--color-text-primary). Nunca nombran un primitivo y nunca nombran un código hex.

En Tailwind v4 esto se mapea limpiamente sobre la nueva directiva @theme, que tanto declara los tokens como genera utilidades a partir de ellos, exponiendo cada token como una variable CSS en tiempo de ejecución. Defines --color-text-primary: var(--color-gray-900) dentro de @theme, usas text-primary en el marcado y sobrescribes la variable bajo un selector .dark en tu capa base. La documentación oficial de variables de tema de Tailwind cubre la mecánica, y la misma disciplina de dos capas se aplica independientemente del framework.

El modo oscuro es un problema de mapeo de tokens, no de color

La razón por la que la capa semántica se gana su sustento es que el modo oscuro se vuelve casi trivial una vez que existe. No tocas un solo componente. Sobrescribes los tokens semánticos dentro de un ámbito .dark (o [data-theme="dark"]) para que resuelvan a primitivos distintos.

En modo claro, --color-bg-surface: var(--white) y --color-text-primary: var(--gray-900). En modo oscuro, bajo el selector .dark, --color-bg-surface: var(--gray-900) y --color-text-primary: var(--gray-100). Cada componente que lee color-bg-surface se voltea automáticamente, porque la cascada hace el trabajo.

Dos cosas que muerden a la gente aquí:

Las trampas que de verdad se rompen a escala

Tras suficientes rebrandings y lanzamientos de temas, los modos de fallo riman:

El resumen honesto es que los tokens no toman las decisiones de color por ti: hacen que las decisiones de color sean baratas de cambiar. La estructura es lo que te compra la capacidad de rehacer la marca un viernes, lanzar el modo oscuro en un sprint y corregir un bug de contraste en una línea en lugar de en cuarenta repositorios. Invierte tu esfuerzo en la capa semántica y en la nomenclatura, mantén a los componentes honestos sobre qué nivel tocan, y el sistema absorberá cambios que ni siquiera puedes predecir todavía.

Preguntas frecuentes

¿Cuál es la diferencia entre los tokens de color primitivos y semánticos?

Un token primitivo (o base) nombra un valor de color en bruto sin contexto, por ejemplo blue-500: #3B82F6. Describe qué es el color. Un token semántico nombra el propósito del color, como color-action-primary o color-feedback-error, y apunta a un primitivo. Los componentes deberían referenciar tokens semánticos, nunca primitivos, de modo que un rebranding o un theming solo requiera reapuntar la capa semántica en lugar de editar cada componente.

¿Cómo debería nombrar los tokens de color en un sistema de diseño?

Nombra los tokens semánticos y de componente por intención, no por apariencia. color-feedback-error sobrevive a un rediseño en el que el rojo de error pasa a ser naranja; color-text-red no. Usa una estructura de lo general a lo específico como categoria-propiedad-variante-estado (p. ej. color-bg-surface-raised, color-action-primary-hover). Los tokens primitivos son la excepción: deberían describir el color literal, como gray-900, porque ese es todo su propósito.

¿Por qué usar una escala numérica de color del 50 al 950?

La escala del 50 al 950 (50, 100, 200 … 900, 950) popularizada por Tailwind se ha convertido en un estándar de la industria, así que resulta instantáneamente familiar para los nuevos ingenieros. 50 es el tinte más claro, ~500 es el tono de marca puro y 950 es casi negro. Espaciar los pasos en un modelo perceptual como HSL u OKLCH —en lugar de mezclar ingenuamente blanco y negro en sRGB— mantiene los pasos con una sensación de espaciado uniforme y evita los medios tonos enturbiados.

¿Cómo facilitan el modo oscuro los tokens de color?

El modo oscuro pasa a ser un problema de mapeo de tokens en lugar de un rediseño. Como los componentes solo leen tokens semánticos, sobrescribes esos tokens dentro de un ámbito .dark o [data-theme=dark] para que resuelvan a primitivos distintos; por ejemplo color-bg-surface apunta a blanco en modo claro y a gray-900 en oscuro. La cascada CSS voltea cada componente automáticamente, sin cambios en el código del componente. Recuerda desaturar los colores de acento y evitar los fondos negro puro para reducir la fatiga visual y la halación.

¿Qué ratios de contraste deben cumplir los tokens de color para la accesibilidad?

La WCAG exige una ratio de contraste de al menos 4,5:1 para el texto normal y 3:1 para el texto grande y los componentes de interfaz. Incorpora estas comprobaciones a tu sistema de tokens validando cada emparejamiento semántico de texto sobre fondo en cada tema. Una combinación que pasa en modo claro puede fallar en modo oscuro, así que prueba ambos, y define un token explícito de texto sobre acción para garantizar que las etiquetas sobre botones saturados sigan siendo legibles.

¿Quieres experimentar con los colores?

Prueba nuestro generador gratuito de paletas de color para encontrar tu armonía perfecta, con un comprobador de contraste WCAG integrado.

Abrir el generador