Sintaxis de colores relativos de CSS

Crea colores nuevos en función de los canales y valores de otro color.

Adam Argyle
Adam Argyle

En Chrome 119, es una función de color muy potente del nivel 5 de color de CSS. La sintaxis de color relativa crea una ruta de acceso fluida para la manipulación de colores dentro de CSS, lo que ofrece a los autores y diseñadores las siguientes posibilidades:

Antes de la sintaxis de color relativa, para modificar la opacidad de un color, debes crear propiedades personalizadas para los canales de un color, por lo general, HSL, y ensamblarlas en un color final y un color de variante final. Esto significa administrar muchas piezas de color, lo que puede volverse una carga rápidamente.

:root {
  --brand-hue: 300deg;
  --brand-saturation: 75%;
  --brand-lightness: 50%;

  --brand-hsl:
    var(--brand-hue)
    var(--brand-saturation)
    var(--brand-lightness);

  --brand-color: hsl(var(--brand-hsl));

  /* all this work just so I can set the opacity to 50% in a variant */
  --brand-color-variant: hsl(var(--brand-hsl) / 50%);
}

Después de la sintaxis de color relativa, puedes crear un color de marca con cualquier espacio de color o sintaxis que necesites y crear una variante de opacidad media con mucho menos código. También es mucho más fácil leer el propósito de los estilos y el sistema.

:root {
  --brand-color: hsl(300deg 75% 50%);
  --brand-color-variant: hsl(from var(--brand-color) h s l / 50%);
}

Esta publicación te ayudará a aprender la sintaxis y demostrar manipulaciones de colores comunes.

Si prefieres el video, este Desafío de GUI abarca casi todo el siguiente artículo.

Descripción general de la sintaxis

El objetivo de la sintaxis de color relativa es permitir derivar un color de otro. El color base se denomina color de origen, que es el color que se muestra después de la nueva palabra clave from. El navegador convertirá y dividirá este color original y ofrecerá las partes como variables para usar en la nueva definición de color.

Se muestra un diagrama de la sintaxis rgb(from green r g b / alpha), con una flecha que sale de la parte superior del verde y se arquea en el comienzo de la función rgb. Esta flecha se divide en 4 flechas que, luego, apuntan a su variable relevante. Las 4 flechas son roja, verde, azul y alfa. El rojo y el azul tienen un valor de 0, el verde es 128 y el alfa es 100%.

En el diagrama anterior, se muestra cómo el color original green se convierte en el espacio de color del color nuevo, que se convierte en números individuales representados como variables r, g, b y alpha, que luego se usan directamente como valores de un nuevo color rgb().

Si bien esta imagen muestra el desglose, el proceso y las variables, tampoco cambia el color. Las variables se vuelven a colocar en el color sin cambios, por lo que el color sigue siendo verde.

La palabra clave from

La primera parte de la sintaxis que debes aprender es la adición de from <color> para especificar un color. Se coloca justo antes de especificar los valores. Este es un ejemplo de código en el que todo lo que se agregó es from green, justo antes de que se especifiquen los valores de rgb().

.syntax-introduction_same-colors {
  color: green;
  color: rgb(0 128 0);
  color: rgb(from green r g b);    /* result = rgb(0 128 0) */
}

Esa palabra clave from, cuando se ve como el primer parámetro en la notación funcional, convierte la definición de color en un color relativo. Después de la palabra clave from, CSS espera un color, un color que inspirará el siguiente.

Conversión de colores

En términos más simples, convierte el verde en canales de r, g y b para usarlos en un color nuevo.

rgb(from green r g b)           /* r=0 g=128 b=0 */
rgb(from rgb(0 128 0) r g b);   /* r=0 g=128 b=0 */

Colores de propiedades personalizadas

La lectura de rgb from green es muy clara y fácil de entender. Es por eso que las propiedades personalizadas y la sintaxis de color relativa son una combinación tan buena, ya que puedes quitar el misterio del color from. Por lo general, tampoco es necesario que conozcas el formato de color de la propiedad personalizada, ya que estás creando un color nuevo en el formato que elijas.

rgb(from rgb(255 105 180) r g b) /* ????? */
rgb(from var(--hotpink) r g b)   /* clear */

Trabaja en tu espacio de color preferido

Puedes elegir el espacio de color con la notación de color funcional que elijas.

rgb(from hsl(120 100% 25%) r g b)     /*  r=0   g=128  b=0    */
hsl(from hsl(120 100% 25%) h s l)     /*  h=120 s=100% l=25%  */
hwb(from hsl(120 100% 25%) h w b)     /*  h=120 w=0%   b=50%  */
lch(from hsl(120 100% 25%) l c h)     /*  l=46  c=68   h=134  */

La sintaxis de color relativa tiene ese paso de conversión. El color después de from se convierte en el espacio de color como se especifica al comienzo del color relativo. La entrada y la salida no tienen que coincidir, lo que es muy liberador.

La capacidad de elegir un espacio de color también es útil, ya que tiende a enfocarse más en el tipo de alternancia de colores que en una preferencia. La preferencia está en los resultados, no en el formato de color ni en los tipos de canales. Esto quedará mucho más claro en las secciones en las que se muestran casos de uso, ya que los diferentes espacios de color se destacan en diferentes tareas.

Combina, haz coincidir, omite y repite las variables

Algo extraño pero emocionante de esta sintaxis es que las variables no se tienen que volver a ordenar y se pueden repetir.

rgb(from green g g g)    /* rgb(128 128 128) */
rgb(from green b r g)    /* rgb(0 0 128) */
rgb(from green 0 0 g)    /* rgb(0 0 128) */

Opacidad como variable

La sintaxis también proporciona la opacidad como una variable llamada alpha. Es opcional y va después de / en la notación de color funcional.

rgb(from #00800080 r g b / alpha)             /* alpha=50% */
rgb(from rgba(0,128,0,.5) r g b / alpha)      /* alpha=50% */
rgb(from rgb(0 128 0 / 50%) r g b / alpha)    /* alpha=50% */

Usa calc() o alguna otra función de CSS en las variables.

Hasta ahora, hemos estado creando el color verde una y otra vez. Aprender la sintaxis y familiarizarse con los pasos de conversión y desestructuración Ahora es el momento de modificar las variables y alterar el resultado para que no sea igual que la entrada.

green                              /*  h=120 s=100% l=25%  */
hsl(from green calc(h * 2) s l)    /*  h=240 s=100% l=25%  */

Ahora es azul marino. Se duplicó el tono, se tomó un tono de 120 y se convirtió en 240, lo que alteró por completo el color. Esto hizo girar el tono a lo largo de la rueda de color, un truco sencillo que se simplifica mucho con los espacios de color cilíndricos, como HSL, HWB, LCH y OKLCH.

Para ver visualmente los valores de los canales, de modo que puedas obtener la matemática correcta sin adivinar ni tener que memorizar las especificaciones, prueba esta herramienta de valores de canales de sintaxis de color relativa. Revela el valor de cada canal según la sintaxis que especifiques, lo que te permite saber exactamente qué valores tienes disponibles para usar.

Cómo verificar la compatibilidad del navegador

@supports (color: rgb(from white r g b)) {
  /* safe to use relative color syntax */
}

Casos de uso y demostraciones

Los siguientes ejemplos y casos de uso tienen muchas sintaxis alternativas para lograr resultados similares o los mismos. Las variaciones provienen de los espacios de color y los canales que ofrecen.

Además, muchos ejemplos mostrarán ajustes de color con la terminología de by y to. Un by de color cambiado es un cambio de color relativo, un cambio que usa el valor de la variable y realiza un ajuste en función de su valor actual. Un to de color cambiado es un cambio de color absoluto, un cambio que no usa el valor de la variable y, en su lugar, especifica un valor completamente nuevo.

Puedes encontrar todas las demostraciones en esta colección de Codepen.

Cómo aclarar un color

Los espacios de color OKLCH, OKLAB, XYZ o sRGB proporcionan los resultados más predecibles cuando se aclaran los colores.

Aclarar en una cantidad

En el siguiente ejemplo, .lighten-by-25 toma el color blue y lo convierte en OKLCH. Luego, aclara el azul aumentando el canal l (luminosidad) multiplicando el valor actual por 1.25. Esto empuja la luminosidad azul hacia el blanco en un 25%.

.lighten-by-25 {
  background: oklch(from blue calc(l * 1.25) c h);
}

Aclarar a un valor específico

En el siguiente ejemplo, .lighten-to-75 no usa el canal l para aclarar blue, sino que reemplaza por completo el valor con 75%.

.lighten-to-75 {
  background: oklch(from blue 75% c h);
}

Oscurecer un color

Los mismos espacios de color que son eficaces para aclarar un color también son excelentes para oscurecerlo.

Oscurecer en una cantidad

En el siguiente ejemplo, .darken-by-25 toma el color azul y lo convierte en OKLCH. Luego, oscurece el azul disminuyendo el canal l (luminosidad) en un 25% multiplicando el valor por .75. Esto empuja el color azul hacia el negro en un 25%.

.darken-by-25 {
  background: oklch(from blue calc(l * .75) c h);
}

Oscurecer hasta un valor especificado

En el siguiente ejemplo, .darken-to-25 no usa el canal l para oscurecer blue, sino que reemplaza por completo el valor con 25%.

.darken-to-25 {
  background: oklch(from blue 25% c h);
}

Cómo saturar un color

Saturación por un importe

En el siguiente ejemplo, .saturate-by-50 usa el s de hsl() para aumentar la intensidad de orchid en un 50% relativo.

.saturate-by-50 {
  background: hsl(from orchid h calc(s * 1.5) l);
}

Saturación a una cantidad específica

En el siguiente ejemplo, .saturate-to-100 no usa el canal s de hsl(), sino que especifica un valor de saturación deseado. En este ejemplo, la saturación se incrementa a 100%.

.saturate-to-100 {
  background: hsl(from orchid h 100% l);
}

Cómo desaturar un color

Desaturar en una cantidad

En el siguiente ejemplo, .desaturate-by-half usa el s de hsl() para disminuir la saturación de indigo a la mitad.

.desaturate-by-half {
  background: hsl(from indigo h calc(s / 2) l);
}

Desaturar a un valor específico

En lugar de desaturar en una cantidad, puedes desaturar hasta un valor específico deseado. En el siguiente ejemplo, .desaturate-to-25 crea un color nuevo basado en indigo, pero establece la saturación en un 25%.

.desaturate-to-25 {
  background: hsl(from indigo h 25% l);
}

Cómo aumentar el croma de un color

Este efecto es similar a saturar un color, pero es diferente en algunos aspectos. En primer lugar, es un cambio de chroma y no de saturation, y esto se debe a que los espacios de color que pueden aumentar al rango dinámico alto no usan saturación. Los espacios de color que incluyen chroma son compatibles con el alto rango dinámico, lo que permite a los autores aumentar la intensidad del color más allá de lo que puede hacer la saturación.

.increase-chroma {
  background: oklch(from orange l calc(c + .1) h);
}

Cómo ajustar la opacidad de un color

Crear una variante semitransparente de un color es uno de los ajustes de color más comunes que se realizan en los sistemas de diseño. Si no lo viste, consulta el ejemplo en la introducción de este artículo, ya que describe muy bien el espacio del problema.

Ajusta la opacidad en una cantidad

.decrease-opacity-by-25 {
  background: rgb(from lime r g b / calc(alpha / 2));
}

Ajusta la opacidad a un valor específico

.decrease-opacity-to-25 {
  background: rgb(from lime r g b / 25%);
}

Cómo invertir un color

La inversión de colores es una función de ajuste de colores común que se encuentra en las bibliotecas de colores. Una forma de lograrlo es convertir un color a RGB y, luego, restar el valor de cada canal de 1.

.invert-each-rgb-channel {
  background: rgb(from yellow calc(255 - r) calc(255 - g) calc(255 - b));
}

Cómo complementar un color

Si tu objetivo no era invertir un color, sino complementarlo, es probable que lo que buscas sea la rotación de tono. Elige un espacio de color que ofrezca el tono como un ángulo y, luego, usa calc() para rotar el tono en la cantidad que desees. Para encontrar el complemento de un color, se rota media vuelta. En este caso, puedes agregar o quitar 180 del canal h para obtener el resultado.

.complementary-color {
  background: hsl(from blue calc(h + 180) s l);
}

Cómo contrastar un color

Como método para lograr relaciones de contraste de colores accesibles, considera L* (Lstar). Esto usa el canal de luminosidad (L) perceptualmente uniforme (aproximadamente) de LCH y OKLCH, en un calc(). Según si te orientas a un contraste bajo, medio o alto, el delta de L&middot; es de alrededor de ~40, ~50 o ~60.

Esta técnica funciona bien en cualquier tono en LCH o OKLCH.

Contrasta un color más oscuro

La clase .well-contrasting-darker-color demuestra L* con un delta de 60. Dado que el color de origen es oscuro (valor de luminosidad bajo), se agrega un 60% (.6) al canal de luminosidad. Esta técnica se usa para encontrar un color de texto oscuro con el mismo tono y buen contraste sobre un fondo claro.

.well-contrasting-darker-color {
  background: darkred;
  color: oklch(from darkred calc(l + .60) c h);
}

Contrasta un color más claro

La clase .well-contrasting-lighter-color también muestra L* con un delta del 60%. Dado que el color de origen es un color claro (valor alto de luminosidad), se resta 0.60 del canal de luminosidad.

.well-contrasting-lighter-color {
  background: lightpink;
  color: oklch(from lightpink calc(l - .60) c h);
}
.

Paletas de colores

La sintaxis de colores relativa es muy buena para crear paletas de colores. Es particularmente útil y potente debido a la cantidad de espacios de color disponibles. En los siguientes ejemplos, se usa OKLCH porque el canal de luminosidad es confiable y el canal de tono se puede rotar sin efectos secundarios. En el ejemplo final, se muestra una combinación de ajustes de luminosidad y rotación de tono para obtener un resultado más interesante.

Abre el código fuente de ejemplo para estas y cambia el --base-color para ver cuán dinámicas son estas paletas. ¡Es divertido!

Si te gustan los videos, en YouTube, explico en detalle cómo crear paletas de colores en CSS con OKLCH.

Paletas monocromáticas

Crear una paleta monocromática consiste en crear una paleta con el mismo tono, pero con variaciones en la luminosidad y la oscuridad. El color del medio es el color de origen de la paleta, en el que se colocan dos variantes más claras y dos más oscuras a cada lado.

:root {
  --base-color: deeppink;

  --color-0: oklch(from var(--base-color) calc(l + .20) c h); /* lightest */
  --color-1: oklch(from var(--base-color) calc(l + .10) c h);
  --color-2: var(--base-color);
  --color-3: oklch(from var(--base-color) calc(l - .10) c h);
  --color-4: oklch(from var(--base-color) calc(l - .20) c h); /* darkest */
}
Prueba una gran cantidad de paletas creadas con sintaxis de color relativa y OKLCH

Open Props, una biblioteca de variables de CSS gratuitas, ofrece paletas de colores creadas con esta estrategia y las hace fáciles de usar con una importación. También se basan en un color que puedes personalizar. Solo debes asignarle un color y se generará una paleta.

Paletas análogas

Dado que la rotación de tono es tan fácil con OKLCH y HSL, es trivial crear una paleta de colores análoga. Rota el tono en una cantidad que te guste y cambia el color base. Luego, observa cómo el navegador crea paletas nuevas.

:root {
  --base-color: blue;

  --primary:   var(--base-color);
  --secondary: oklch(from var(--base-color) l c calc(h - 45));
  --tertiary:  oklch(from var(--base-color) l c calc(h + 45));
}
.

Paletas tríadicas

Al igual que los colores complementarios, las paleta de colores tríadicas son rotaciones de tonos opuestos pero armoniosos dados un color base. Cuando un color complementario está en el lado opuesto de un color, como una línea recta dibujada en el medio del círculo cromático, las paletas triádicas son como un triángulo de líneas, en el que se encuentran 2 colores igualmente rotados a partir de un color base. Para ello, rota el tono 120deg.

Esta es una ligera simplificación de la teoría del color, pero es suficiente para comenzar a usar las paletas triádicas más complejas si te interesa.

:root {
  --base-color: yellow;
  --triad-1: oklch(from var(--base-color) l c calc(h - 120));
  --triad-2: oklch(from var(--base-color) l c calc(h + 120));
}

Paletas tetradic

Las paletas tetradic son cuatro colores divididos de manera uniforme alrededor de la rueda de colores, lo que crea una paleta sin un valor dominante claro. También puedes pensar en ello como dos parejas de colores complementarios. Si se usa con prudencia, puede ser muy significativo.

Esta es una ligera simplificación de la teoría del color, pero es suficiente para comenzar a usar las paletas tetradic más complejas si te interesa.

:root {
  --base-color: lime;

  --color-1: var(--base-color);
  --color-2: oklch(from var(--base-color) l c calc(h + 90));
  --color-3: oklch(from var(--base-color) l c calc(h + 180));
  --color-4: oklch(from var(--base-color) l c calc(h + 270));
}

Monocromática con una ligera rotación de tono

Muchos expertos en color tienen este truco bajo la manga. El problema es que una escala de colores monocromática puede ser bastante aburrida. La solución es agregar una rotación de tono menor o mayor a cada color nuevo a medida que cambia la luminosidad.

En el siguiente ejemplo, se disminuye la luminosidad en un 10% en cada muestra y también se rota el tono en 10 grados. El resultado es una paleta de rosa intenso a índigo que parece combinarse sin problemas como lo haría un gradiente.

:root {
  --base-color: deeppink;

  --color-1: var(--base-color);
  --color-2: oklch(from var(--base-color) calc(l - .10) c calc(h - 10));
  --color-3: oklch(from var(--base-color) calc(l - .20) c calc(h - 20));
  --color-4: oklch(from var(--base-color) calc(l - .30) c calc(h - 30));
  --color-5: oklch(from var(--base-color) calc(l - .40) c calc(h - 40));
}
Prueba esta tabla de clasificación creada con OKLCH y la rotación de tono

La siguiente interfaz de tabla de clasificación usa esta estrategia de rotación de tono. Cada elemento de la lista hace un seguimiento de su índice en el documento como una variable llamada --i. Luego, se usa este índice para ajustar la cromaticidad, la luminosidad y el tono. El ajuste es solo de un 5% o 5 grados, mucho más sutil que el ejemplo anterior con el color rosa oscuro, por lo que se necesita un ojo agudo para notar el motivo por el que esta tabla de clasificación puede estar en cualquier tono con tanta elegancia.

Asegúrate de cambiar el tono en el control deslizante debajo de la tabla de clasificación y observa cómo la sintaxis de colores relativa crea hermosos momentos de color.

li {
  --_bg: oklch(
    /* decrease lightness as list grows */
    calc(75% - (var(--i) * 5%))

    /* decrease chroma as list grows */
    calc(.2 - (var(--i) * .01))

    /* lightly rotate the hue as the list grows */
    calc(var(--hue) - (var(--i) + 5))
  );
}
.