Ir al contenido

El medio de los artesanos de la web miércoles, 19 de agosto de 2026

MailStudio
Diseño y UX

Design tokens en CSS: el método para un tema claro/oscuro mantenible

Dos capas de tokens, una redefinición semántica y tres líneas de script: el método que evita los ficheros dark.css que divergen y los !important en cascada.

Design tokens en CSS : ma méthode pour un thème clair/sombre maintenable

Los temas oscuros añadidos a posteriori dejan un rastro reconocible: una variable aquí, un !important allá, un fichero dark.css duplicado que diverge del tema claro sprint tras sprint. El problema no es el color, es la ausencia de estructura. Los design tokens expresados como propiedades personalizadas CSS aportan esa estructura, sin dependencia ni capa JavaScript.

Un token no es un color

La confusión más extendida consiste en asimilar un valor bruto (#1e40af) a un token. Un design token es una intención con nombre: «el color del texto principal», no «un azul oscuro». De ahí una organización en dos capas. La capa primitiva contiene los valores brutos, nunca usados directamente por los componentes. La capa semántica contiene las intenciones, y es la única que tocan los selectores.

:root {
  /* Capa 1: primitivas (nunca usadas tal cual) */
  --gray-900: #111418;
  --gray-50:  #f7f8fa;
  --blue-500: #2563eb;
  --blue-300: #93c5fd;

  /* Capa 2: semántica (la API de los componentes) */
  --color-bg:      var(--gray-50);
  --color-text:    var(--gray-900);
  --color-accent:  var(--blue-500);
}

El beneficio en mantenimiento es inmediato: un cambio de tono de marca se trata en las primitivas, un problema de contraste detectado en una auditoría de accesibilidad se trata en la capa semántica. Los componentes no se mueven: consumen var(--color-text).

Un componente que referencia un color bruto es deuda técnica que se ignora. Un componente que referencia un token semántico es un contrato.

El modo oscuro como redefinición

Una vez establecida esa separación, el modo oscuro se convierte en una operación sin relieve, que es exactamente el objetivo. No se duplica nada: solo se redefine la capa semántica. Las primitivas permanecen, los componentes permanecen, solo cambia la tabla de correspondencia.

1. Respetar la preferencia del sistema

El punto de partida es prefers-color-scheme, que traduce la preferencia expresada por el usuario en su sistema operativo. Ignorarla equivale a pasar por encima de un ajuste explícito.

@media (prefers-color-scheme: dark) {
  :root {
    --color-bg:     var(--gray-900);
    --color-text:   var(--gray-50);
    --color-accent: var(--blue-300);
  }
}

2. Permitir una elección explícita

Un atributo colocado en el elemento raíz permite anular la preferencia del sistema cuando el usuario cambia manualmente. El orden de las reglas determina la prioridad: la elección explícita debe imponerse en ambos sentidos.

:root[data-theme='dark'] { /* mismas redefiniciones */ }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme='light']) { /* mismas redefiniciones */ }
}

3. Evitar el destello al cargar

La única línea de JavaScript realmente necesaria es un script bloqueante situado en el <head>, que aplica el tema memorizado antes del primer renderizado. Cualquier otro enfoque produce un destello de tema claro en las páginas cargadas en modo oscuro.

<script>
  try {
    var t = localStorage.getItem('theme');
    if (t) document.documentElement.setAttribute('data-theme', t);
  } catch (e) {}
</script>
Punto de vigilancia: declarar color-scheme: light u dark en :root no es un detalle. Es lo que indica al navegador que adapte los controles de formulario, las barras de desplazamiento y los campos nativos. Sin esa declaración, un tema oscuro siempre queda delatado por sus <select>.

Lo que el método evita

Síntoma frecuenteCausaRespuesta con tokens
Fichero dark.css que divergeDuplicación de reglasUna sola hoja, redefinición semántica
Acumulación de !importantConflictos de especificidadLos componentes solo leen una variable
Contraste insuficiente en oscuroColores elegidos caso por casoCorrección centralizada en un token
Destello claro al cargarTema aplicado tras el renderizadoScript bloqueante en el <head>

Lo que hay que retener

Un tema claro/oscuro mantenible no exige ni framework ni capa JavaScript: dos capas de tokens, una redefinición semántica y un script de tres líneas contra el destello inicial. Lo esencial del trabajo es una decisión de arquitectura tomada antes de escribir la primera regla de estilo.

Es el método que aplico en mis proyectos de cliente desde hace varios años, y el tema de este sitio es una aplicación directa. La regla que ya no negocio: ningún componente referencia una primitiva. El día que esa regla salta, la deuda vuelve en seis meses. — Simon Janvier

También en Mail Studio

Compartir LinkedIn Bluesky Hacker News E-mail

Leer también