Temas de color con funciones de CSS de Baseline

David A. Herron
David A. Herron

Publicado: 11 de diciembre de 2025

Entonces, tienes un sitio que quieres crear o rediseñar. Tal vez tengas en mente algunos colores principales y estés pensando en cómo implementar rápidamente un tema basado en esos colores.

Necesitarás tu color principal, pero también colores para acciones, estados de desplazamiento, errores y otros usos en la interfaz de usuario. ¿Qué sucede con las opciones de modo claro y oscuro? De repente, necesitarás muchos colores, y puede ser abrumador.

La buena noticia es que, cuando se trata de crear una paleta en relación con los tokens de color que definen tu sitio y cambiar entre los modos de color, las funciones de Baseline pueden hacer gran parte del trabajo pesado por ti. Puedes explorar algunas de estas técnicas en la demostración destacada, una playlist con temas de color en el sitio ficticio de Baseline Radio.

Cómo compilar una base con colores relativos

Si tienes una idea para un color principal de tu tema, con algo de teoría básica del color y la sintaxis de color relativa de CSS, puedes comenzar a generar rápidamente una paleta de colores para usar en tu tema.

Supongamos que tu color base es un tono de verde azulado, que primero puedes definir en tu formato de color preferido. Luego, puedes usar cualquier función de color para crear colores nuevos en relación con tu color base:

html {
  --base-color: oklch(43.7% 0.075 224);
}

La propiedad personalizada --base-color se crea con la función de color oklch(). OklCh es la forma cilíndrica del espacio de color Oklab y define valores para tres canales: L (luminosidad), C (croma), H (tono) y un canal alfa opcional para controlar la transparencia.

OkLCh es un buen formato para este tipo de manipulación de color, ya que está diseñado para proporcionar uniformidad perceptual. Por ejemplo, si solo ajustas el tono de un color, el color resultante debería tener una luminosidad y una croma percibidas similares a las del color original. Esto es especialmente útil para evitar problemas de contraste inesperados.

Si mantienes la misma luminosidad y croma de tu --base-color, puedes ajustar el tono en 120 grados en ambas direcciones para obtener una paleta triádica.

html {
  /* ... */
  --triadic-color-primary: oklch(from var(--base-color) l c calc(h + 120));
  --triadic-color-secondary: oklch(from var(--base-color) l c calc(h - 120));
}

Como se muestra aquí, la sintaxis de color relativa usa una función de color que hace referencia a un color de origen (--base-color en este ejemplo) con la palabra clave from y ajusta los canales respectivos del espacio de color según el color de salida elegido, que en este caso también será OkLCh.

El resultado te da un rosa oscuro para el --accent-color y un tono dorado para usar en el --highlight-color, ambos con la misma luminosidad y croma que el --base-color original.

html {
  /* ... */
  --accent-color: var(--triadic-color-primary);
  --highlight-color: var(--triadic-color-secondary);
}

  html {
    /* Input color in the rgb color space*/
    --base-color: teal;

     /* Output color in oklch. Computes to oklch(0.543123 0.0927099 314.769) */
     --triadic-color-primary: oklch(from var(--base-color) l c calc(h + 120));
  }

Un color complementario agregaría 180 grados al ángulo de tono.

html {
  /* ... */
  --complement-color: oklch(from var(--base-color) l c calc(h + 180));
  --border-highlight: var(--complement-color);
}

Para un estado de desplazamiento en la IU, es posible que desees generar una versión más clara de un color en particular. Esto significaría aumentar el valor del canal de luminosidad. Para un estado activo, puedes agregar transparencia ajustando el canal alfa o oscurecerlo disminuyendo el valor del canal de luminosidad.

html {
  /* Darken the --base-color by 15% */
  --base-color-darkened: oklch(from var(--base-color) calc(l * 0.85) c h);
  /* Assign this color a meaningful variable name */
  --action-color: var(--base-color-darkened);
  /* Lighten the --action-color by 15% */
  --action-color-light: oklch(from var(--action-color) calc(l * 1.15) c h);
  /* Darken the --action-color by 10% */
  --action-color-dark: oklch(from var(--action-color) calc(l * 0.9) c h);
}

Aquí, derivamos el --action-color del --base-color y lo usamos para botones y vínculos. El --action-color tiene dos variantes, más clara y más oscura, que se aplicarían incluso si el --action-color se cambiara para que sea relativo a otro color diferente del --base-color.

Puedes ajustar los canales con una función matemática, como calc(), o reemplazar el canal por completo con un valor nuevo. Los canales sin cambios se representan con sus respectivas letras (por ejemplo, l para un valor de luminosidad sin cambios).

Mezcla colores con color-mix()

Para otras variantes de color, puedes adoptar un enfoque similar y ajustar otros canales de la propiedad personalizada --base-color. También puedes usar color-mix() para agregar toques del color base a otros aspectos de tu diseño.

El color --border-color es una combinación del color base y el color con nombre grey, interpolado en el espacio de color oklab. Cuando se usa como método de interpolación de color, proporciona resultados perceptualmente uniformes.

html {
  --base-mix-grey-50: color-mix(in oklab, var(--base-color), grey);
  --border-color: var(--base-mix-grey-50);
}

De forma predeterminada, sería el 50% de cada color, pero puedes hacer que uno de los colores sea más o menos prominente ajustando su peso porcentual.

html {
  --background-mix-base-80: color-mix(in oklab,
    var(--background-color) 80%,
    var(--base-color));
  --surface-light: var(--background-mix-base-80);
}

Una alternativa para agregar más color a un elemento sería ajustar su canal de croma con la sintaxis de color relativa. El borde de las entradas de texto del formulario de contacto tiene un borde ligeramente más colorido cuando está enfocado.

[data-input*="text"] {
  --focus-ring: transparent;
  /* ... */
  &:focus {
    --focus-ring: oklch(from var(--border-color) l calc(c + 0.1) h);
  }
}

Cómo habilitar los modos claro y oscuro

Una vez que tengas un conjunto de colores con los que trabajar, necesitarás una forma eficiente de aplicar diferentes colores para los modos claro y oscuro.

Compatibilidad con la señalización de temas claros y oscuros con la propiedad color-scheme

Puedes indicarle al navegador de inmediato que tu sitio se puede ver en modo "claro", "oscuro" o ambos con la propiedad color-scheme. Esta propiedad le indica al navegador en qué esquemas de color se puede renderizar un elemento de forma cómoda.

 html {
   color-scheme: light dark;
}

Configuración de color-scheme: light dark en el seudoelemento :root o el elemento html:

  • Indica al navegador que tu página admite la visualización en modo oscuro o claro.
  • Cambia los colores predeterminados de la interfaz de usuario del navegador para que coincidan con el parámetro de configuración del sistema operativo correspondiente.

Para que los agentes de usuario sepan antes que tu página admite los modos claro y oscuro, también puedes indicar que se admite el cambio de esquema de color agregando un elemento <meta> en el <head> del documento.

<head>
  <!-- ... -->
   <meta name="color-scheme" content="light dark">
</head>

Cómo establecer variantes "clara" y "oscura" con la función light-dark()

Como autor, es posible que estés acostumbrado a establecer los colores de una página con una consulta prefers-color-scheme @media.

@media (prefers-color-scheme: light) {
  html {
    --background-color: oklch(95.5% 0 162);
    --text-color: black;
  }
}

@media (prefers-color-scheme: dark) {
  html {
    --background-color: oklch(22.635% 0.01351 291.83);
    --text-color: white;
  }
}

Esto funciona muy bien para los colores y los estilos controlados por el autor, pero, como se mencionó en la sección anterior, aún necesitarías color-scheme para actualizar los colores de la IU del navegador.

Cambiar los colores de la página con una consulta prefers-color-scheme también implica cierta duplicación de código, ya que debes definir los colores para cada modo por separado.

Sin embargo, con color-scheme establecido en la página general (o en elementos específicos), puedes usar la función light-dark() para establecer colores para cada modo en una sola línea de código.

La función acepta dos colores. El primero se usa cuando el esquema de color se establece en "claro", y el segundo, cuando se establece en "oscuro".

html {
  color-scheme: light dark;
  /* Color custom property values for both light and dark modes */
  --base-color: light-dark(oklch(43.7% 0.075 224), oklch(89.2% 0.069 224));
  --background-color: light-dark(oklch(95.5% 0 162), oklch(22.635% 0.01351 291.83));
  --accent-color: oklch(from var(--base-color) l c calc(h + 120));
  --active-color: light-dark(var(--action-color-light), var(--action-color-dark));
  /* ... */
}

Al igual que con cualquier propiedad personalizada, la configuración de light-dark() para tus colores se puede establecer de forma global o dentro de componentes específicos y, luego, usarse en otro lugar según sea necesario.

/* custom property usage */
body {
  background-color: var(--background-color);
  /* ... */
}

:any-link {
  /* ... */
  text-decoration-color: var(--accent-color);
}

Bríndales el control a los usuarios con el conmutador de temas integrado

Es genial tener un tema que se adapte a las preferencias de color predeterminadas del sistema o del navegador de un usuario, pero puedes ir un paso más allá y brindar a los usuarios de tu sitio la capacidad de anular estas preferencias de color predeterminadas.

Si compilas un botón de activación de tema que actualiza el atributo data-scheme en el elemento <html>, puedes usar el mismo atributo para cambiar el color-scheme con CSS.

html {
  color-scheme: light dark;

  &[data-scheme="light"] {
    color-scheme: light;
  }

  &[data-scheme="dark"] {
    color-scheme: dark;
  }

  &[data-scheme="green"] {
      --base-color-light: oklch(48.052% 0.11875 151.945);
      --base-color-dark: oklch(92.124% 0.13356 151.558);
      color-scheme: light dark;
   }
}

data-scheme="light" y data-scheme="dark" muestran la página solo en sus respectivos modos de color. data-scheme="green" se puede ver en cualquiera de los modos y también cambia el --base-color a un tono de verde, lo que te brinda una paleta completamente nueva, ya que la mayoría de los otros colores se basan en el --base-color.

Registra propiedades personalizadas con @property

Hasta ahora, los colores de la demostración se establecieron como propiedades personalizadas estándar. También puedes registrar propiedades con la regla @property para aprovechar los beneficios que ofrece la verificación de tipos.

Dado que el --base-color se usa como base de muchos otros colores de la interfaz, sería bueno asegurarse de que siempre sea un color y tenga un valor de resguardo.

@property --base-color-light {
  syntax: '<color>';
  inherits: false;
  initial-value: oklch(43.7% 0.075 224);
}

@property --base-color-dark {
  syntax: '<color>';
  inherits: false;
  initial-value: oklch(89.2% 0.069 224);
}

html {
  --base-color: light-dark(var(--base-color-light), var(--base-color-dark));
}

De esta manera, si --base-color se cambia inadvertidamente a un valor no válido, siempre volverá a su initial-value establecido con la regla @property.

Registrar ciertas propiedades de esta manera también permite animar los colores sin problemas en un linear-gradient().

.main-heading {
  background: linear-gradient(in oklch 90deg, var(--text-color) 50%, oklch(from var(--base-color) l c var(--header-hue)));
  background-clip: text;
  color: transparent;
  animation: header-hue-switch 5s ease-in-out infinite alternate;
}

.main-heading tiene un fondo linear-gradient() que se muestra a través del texto transparente con la propiedad background-clip.

Una parte del texto muestra un hue que, con la sintaxis de color relativa, se anima desde un valor de canal de 26.67 a 277:

@keyframes header-hue-switch {
  from {
    --header-hue: 26.67;
  }

  to {
    --header-hue: 277;
  }
}

Con una propiedad personalizada --header-hue registrada, esta animación puede ocurrir sin problemas, ya que el navegador sabe que esta propiedad personalizada es un número.

@property --header-hue {
  syntax: '<number>';
  inherits: false;
  initial-value: 100;
}

Con una propiedad personalizada no registrada, el navegador no conocería el tipo de datos de --header-hue, por lo que la transición a un número sería una animación discreta, que saltaría entre estados sin interpolación gradual.

Conclusión

Las nuevas herramientas de Baseline pueden ayudarte a crear rápidamente una paleta de colores ajustable y hacer que el proceso de creación de variables de color sea más eficiente. Sin embargo, aún tendrás que preocuparte por las infinitas opciones y combinaciones de colores.

Crear tu paleta de forma dinámica como esta te brinda flexibilidad. Si necesitas cambiar el color base para la marca, solo tienes que actualizar --base-color, y el resto del tema se derivará de ese color. O bien, si agregas funciones de reproducción de música, podrías decidir cambiar de forma dinámica el color base para que coincida con la canción que se está reproduciendo.

Créditos

La lógica del selector de temas se adaptó del componente de cambio de tema de Adam Argyle.