A los desarrolladores les encantan las victorias rápidas. Cuando el ticket dice "añadir modo oscuro", el camino de menor resistencia parece obvio: escribir light.css, escribir dark.css y alternar entre ellos. Se siente limpio. Se entrega rápido. Para un pequeño proyecto paralelo con tres componentes, incluso podría funcionar. Pero una vez que tu aplicación crece más allá de un puñado de módulos, ese segundo archivo deja de ser un activo y se convierte en una carga que mantienes por duplicado.

La trampa de los dos archivos

La lógica parece sólida a primera vista. Separación de responsabilidades, ¿verdad? Las cosas claras por aquí, las cosas oscuras por allá. Abres dos archivos en tu editor. Copias los estilos de las tarjetas del archivo claro al archivo oscuro, cambias #ffffff por #1a1a1a y terminas el trabajo.

El problema no es la primera semana. El problema es el sexto mes, cuando un diseñador pide un radio de borde ligeramente diferente en el botón principal, o cuando el equipo de producto quiere un nuevo estado de advertencia en el formulario de pago. Actualizas la hoja de estilos clara. Le echas un vistazo rápido a la hoja de estilos oscura. Quizás recuerdes copiar el cambio. Quizás no. En ese desfase es donde muere la calidad. Ya no estás manteniendo una interfaz. Estás manteniendo dos interfaces paralelas que casualmente comparten el mismo esqueleto HTML.

La deriva de temas es inevitable

Este desfase tiene un nombre que los equipos de frontend están empezando a reconocer: la deriva de temas (theme drift). Ocurre cuando tus dos hojas de estilos evolucionan a diferentes velocidades. Un ajuste de padding aquí. Un pequeño cambio en la sombra allá. El archivo oscuro se convierte en el hermano descuidado. O peor aún, se convierte en una fuente de temor. Los desarrolladores empiezan a evitar los cambios porque tocar un tema significa buscar en otro archivo para duplicar el trabajo.

La carga cognitiva se acumula rápidamente. Querías escribir CSS una vez. En su lugar, lo escribiste dos veces, y ahora estás pagando intereses por esa deuda cada vez que el sistema de diseño cambia. Los iconos se desalinean en el modo oscuro porque alguien actualizó un flex gap en el archivo claro y olvidó replicarlo. Los anillos de enfoque desaparecen porque una nueva regla de accesibilidad solo se incluyó en una de las hojas. La interfaz de usuario no solo se ve mal; empieza a sentirse rota.

Usa tokens semánticos

La solución no es una mejor herramienta de diff o una revisión de código más estricta. La solución es una forma diferente de pensar en el color. Deja de organizar tus estilos por apariencia literal y empieza a organizarlos por propósito. Aquí es donde entran en juego los tokens semánticos.

En lugar de asignar un fondo blanco a una tarjeta, asígnale un fondo de superficie (surface background). En lugar de elegir entre negro y blanco roto para el texto, elige un color de texto. El componente no sabe ni le importa si el usuario prefiere el modo claro o el oscuro. Simplemente solicita el token que corresponde a su función.

Piensa en un botón estándar. En un mundo de dos archivos, .btn vive en la hoja de estilos clara con un fondo blanco y un borde oscuro. Su gemelo vive en la hoja de estilos oscura con un fondo casi negro y un borde más claro. Eso es el doble de código para un solo botón. Con tokens, .btn tiene una única declaración: el fondo es var(--color-surface-secondary) y el borde es var(--color-border-default). Los valores en sí mismos residen en la raíz. Cuando el sitio está en modo claro, --color-surface-secondary se resuelve en algo como #f8f9fa. En modo oscuro, el mismo token se resuelve en #2d2d2d. El componente del botón nunca cambia. Solo cambian los datos subyacentes.

Esta distinción entre estructura y datos es sutil pero poderosa. Tu componente de tarjeta define el diseño, el espaciado, la tipografía y la elevación una sola vez. Tu capa de tema define la paleta. Esa separación es exactamente para lo que se crearon las propiedades personalizadas de CSS (CSS custom properties).

Cómo cambia la arquitectura

Este enfoque reestructura fundamentalmente la forma en que escribes estilos.

La forma antigua suele ser así:

  • Una hoja de estilos para tarjetas claras que define padding, radio, fondo, color de texto y sombra.
  • Una hoja de estilos para tarjetas oscuras que redefine la mayoría de las mismas propiedades solo para invertir los colores.
  • Una capa de lógica que decide qué hoja de estilos cargar o qué clase alternar en el body.

La nueva forma es así:

  • Una hoja de estilos para tarjetas que define el diseño y asigna tokens semánticos.
  • Un archivo de tema que define qué significan esos tokens en un contexto claro.
  • Un archivo de tema, o simplemente un bloque en el mismo archivo, que define qué significan esos tokens en un contexto oscuro.
  • Un único cambio de atributo que modifica la capa de valores sin tocar la capa del componente.

Mantienes la configuración estable. Solo cambias los datos. Cuando el diseñador quiera introducir un tercer tema, tal vez un modo de alto contraste o una variante azul medianoche, no tienes que reescribir la tarjeta. Solo añades una asignación más al mapa de tokens. El componente se mantiene simple y funcional. Sigue necesitando un color de superficie. El tema le indica qué color de superficie usar.

El cambio mediante atributos de datos

La implementación puede seguir siendo sencilla y legible. Aplica un atributo de datos a tu etiqueta HTML, algo como data-theme="dark", y deja que tus definiciones de tokens se limiten a ese ámbito.

Establece tus valores por defecto en :root para la experiencia clara, de modo que la página se renderice correctamente antes de que se ejecute JavaScript. Luego, sobrescribe los valores de los tokens bajo [data-theme="dark"]. Un pequeño script vigila el clic en el interruptor, actualiza el atributo y cada componente de la página responde al instante. Sin cambios constantes de clases en elementos individuales. Sin importar una hoja de estilos completamente distinta a mitad del renderizado. El navegador ya tiene las variables en memoria; simplemente vuelve a pintar con los nuevos valores.

Esto mantiene tu código limpio en un sentido muy práctico. No tienes que usar grep en dos directorios para encontrar cada instancia de .card. No tienes que preocuparte por guerras de especificidad entre clases de temas competidoras apiladas en el mismo nodo. Tu HTML sigue siendo legible. Tu CSS se mantiene centralizado y fácil de buscar.

Se trata de valores, no de versiones

El modo oscuro trata sobre valores. No es una segunda versión de tu interfaz de usuario. Las esquinas de tu tarjeta no se vuelven más redondeadas por la noche. Tu cuadrícula no se colapsa en una forma diferente. Tu escala tipográfica no necesita un nuevo ritmo. Solo cambian los colores y, a veces, las sombras respiran un poco más profundo. Tratar la oscuridad como un rediseño completo es una sobreingeniería que crea pesadillas de mantenimiento.

Los equipos que hacen esto bien tratan su sistema de diseño como una base de datos. Los componentes consultan propiedades por nombre. Los temas proporcionan los registros. Cambiar de claro a oscuro es un cambio de parámetro de consulta, no una reescritura del esquema.

Esa mentalidad es lo que te salva de la deriva de temas. Una tarjeta. Un botón. Una única fuente de verdad para el espaciado y el tamaño. La paleta vive en un solo lugar, mapeada lógicamente, lista para cualquier entorno que el usuario prefiera.

La conclusión principal

Si estás manteniendo dos archivos CSS para el modo claro y oscuro, no estás aplicando temas. Estás clonando. Pásate a los tokens semánticos, delimita su alcance con un atributo de datos a nivel de raíz y deja que tus componentes soliciten roles en lugar de apariencias codificadas de forma fija. La refactorización inicial requiere esfuerzo, pero la alternativa es un juego interminable de "atrapa al topo" entre hojas de estilo paralelas. La vida es demasiado corta para escribir la misma tarjeta dos veces.