Sintaxe de cores relativa de CSS

Crie novas cores com base nos canais e valores de outra cor.

No Chrome 119, há um recurso de cor muito poderoso do CSS Color Level 5. A sintaxe de cor relativa cria um caminho suave para a manipulação de cores no CSS, oferecendo maneiras de autores e designers:

Antes da sintaxe de cor relativa, para modificar a opacidade de uma cor, é necessário criar propriedades personalizadas para os canais de uma cor, geralmente HSL, e montá-las em uma cor final e uma cor de variante final. Isso significa gerenciar muitas peças coloridas, o que pode se tornar uma tarefa pesada.

: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%);
}

Depois da sintaxe de cor relativa, você pode criar uma cor de marca com qualquer espaço de cor ou sintaxe necessária e criar uma variante de meia opacidade com muito menos código. Também fica muito mais fácil ler a intenção dos estilos e do sistema.

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

Esta postagem vai ajudar você a aprender a sintaxe e demonstrar manipulações de cores comuns.

Se você preferir vídeo, quase todo o artigo a seguir é abordado neste Desafio de GUI.

Visão geral da sintaxe

O objetivo da sintaxe de cor relativa é permitir a derivação de uma cor de outra cor. A cor base é chamada de cor de origem, que é a cor que vem depois da nova palavra-chave from. O navegador converte e separa essa cor de origem, oferecendo as partes como variáveis para uso na nova definição de cor.

Um
diagrama da sintaxe rgb(from green r g b / alpha) é mostrado, com uma seta
saindo da parte de cima do verde e arqueando para o início do RGB da função.
Essa seta se divide em quatro setas que apontam para a variável relevante. As
quatro setas são vermelha, verde, azul e alfa. O vermelho e o azul têm um valor de 0, o verde
é 128 e o alfa é 100%.

O diagrama anterior mostra a cor de origem green sendo convertida para o espaço de cor da nova cor, transformada em números individuais representados como variáveis r, g, b e alpha, que são usadas diretamente como novos valores de cor rgb().

Embora essa imagem mostre a divisão, o processo e as variáveis, ela também não muda a cor. As variáveis são colocadas de volta na cor sem alterações, resultando em uma cor verde.

A palavra-chave from

A primeira parte da sintaxe a ser aprendida é a adição de from <color> para especificar uma cor. Ele é exibido antes de você especificar os valores. Confira um exemplo de código em que tudo o que foi adicionado é from green, logo antes de os valores de rgb() serem especificados.

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

Essa palavra-chave from, quando considerada o primeiro parâmetro na notação funcional, transforma a definição de cor em uma cor relativa. Depois da palavra-chave from, o CSS espera uma cor, uma cor que vai inspirar a próxima cor.

Conversão de cores

Em termos mais simples, ele converte o verde em canais r g e b para uso em uma nova cor.

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 */

Cores de propriedades personalizadas

A leitura de rgb from green é muito clara e fácil de entender. É por isso que propriedades personalizadas e sintaxe de cor relativa são uma combinação tão boa, porque você pode descobrir a cor from. Também não é necessário conhecer o formato de cor da propriedade personalizada, já que você está criando uma nova cor no formato que preferir.

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

Trabalhar no espaço de cores de sua preferência

Você pode escolher o espaço de cores com a notação de cor funcional que preferir.

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  */

A sintaxe de cor relativa tem essa etapa de conversão. A cor após from é convertida no espaço de cores conforme especificado no início da cor relativa. A entrada e a saída não precisam ser iguais, o que é muito libertador.

A capacidade de escolher um espaço de cores também é importante, já que a escolha de um espaço de cores tende a ser mais focada no tipo de alternância de cores do que em uma preferência. A preferência está nos resultados, não no formato de cor ou nos tipos de canal. Isso vai ficar muito mais claro nas seções que demonstram casos de uso, já que espaços de cores diferentes se destacam em tarefas diferentes.

Misture, combine, omita e repita as variáveis

Algo estranho, mas interessante nessa sintaxe, as variáveis não precisam ser colocadas de volta na ordem e podem ser repetidas.

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) */

Opacidade como uma variável

A sintaxe também fornece a opacidade como uma variável chamada alpha. Ela é opcional e fica depois do / na notação funcional de cores.

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% */

Use calc() ou outras funções CSS nas variáveis.

Até agora, criamos a cor verde várias vezes. Aprender a sintaxe, se familiarizar com as etapas de conversão e de desestruturação. Agora é hora de modificar as variáveis, alterando a saída para que ela não seja igual à entrada.

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

Agora é azul-marinho! O matiz foi duplicado, transformando um matiz de 120 em 240, alterando completamente a cor. Isso fez com que a matiz girasse ao longo da roda de cores, um truque simples com espaços de cores cilíndricos como HSL, HWB, LCH e OKLCH.

Para conferir visualmente os valores dos canais e fazer os cálculos sem precisar adivinhar ou memorizar as especificações, use esta ferramenta de valores de canal de sintaxe de cor relativa. Ele revela o valor de cada canal com base na sintaxe especificada, permitindo que você saiba exatamente quais valores estão disponíveis.

Verificar o suporte do navegador

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

Casos de uso e demonstrações

Os exemplos e casos de uso a seguir têm muitas sintaxe alternativas para alcançar resultados semelhantes ou iguais. As variações vêm dos espaços de cor e dos canais que eles oferecem.

Além disso, muitos exemplos mostram ajustes de cor com a linguagem de by e to. Uma cor alterada by é uma mudança de cor relativa, uma mudança que usa o valor da variável e faz um ajuste com base no valor atual. Uma mudança de cor to é uma mudança de cor absoluta, uma mudança que não usa o valor da variável e especifica um valor completamente novo.

Todas as demonstrações podem ser encontradas nesta coleção do Codepen.

Iluminar uma cor

Os espaços de cores OKLCH, OKLAB, XYZ ou sRGB oferecem os resultados mais previsíveis ao clarear cores.

Diminuir por um valor

O exemplo .lighten-by-25 a seguir converte a cor blue em OKLCH e, em seguida, clareia o azul aumentando o canal l (luminosidade) multiplicando o valor atual por 1.25. Isso aumenta a luminosidade azul em 25% em relação ao branco.

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

Iluminar para um valor específico

O exemplo .lighten-to-75 a seguir não utiliza o canal l para clarear blue, mas substitui completamente o valor por 75%.

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

Escurecer uma cor

Os mesmos espaços de cores eficazes para clarear uma cor também são ótimos para escurecer.

Escurecer por um valor

O exemplo .darken-by-25 a seguir converte a cor azul em OKLCH e, em seguida, escurece o azul diminuindo o canal l (luminosidade) em 25% ao multiplicar o valor por .75. Isso empurra a cor azul em direção ao preto em 25%.

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

Escurecer para um valor especificado

O exemplo .darken-to-25 a seguir não utiliza o canal l para escurecer blue, mas substitui completamente o valor por 25%.

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

Saturar uma cor

Saturar por um valor

O exemplo de .saturate-by-50 a seguir usa o s de hsl() para aumentar a vibração de orchid por um 50% relativo.

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

Saturar até um valor específico

O exemplo de .saturate-to-100 a seguir não utiliza o canal s de hsl(), mas especifica um valor de saturação desejado. Neste exemplo, a saturação é elevada para 100%.

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

Desaturar uma cor

Desaturar por um valor

O exemplo .desaturate-by-half a seguir usa o s de hsl() para reduzir a saturação de indigo pela metade.

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

Desaturar para um valor específico

Em vez de desaturar por uma quantidade, você pode desaturar para um valor específico. O exemplo .desaturate-to-25 a seguir cria uma nova cor com base em indigo, mas define a saturação como 25%.

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

Realce de cor

Esse efeito é semelhante à saturação de uma cor, mas é diferente em alguns aspectos. Primeiro, é uma mudança de chroma, e não de saturation, porque os espaços de cores que podem aumentar o alcance dinâmico alto não usam saturação. Os espaços de cores que têm chroma são compatíveis com o alto alcance dinâmico, permitindo que os autores aumentem a vibração das cores além do que a saturação permite.

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

Ajustar a opacidade de uma cor

Criar uma variante semitransparente de uma cor é um dos ajustes de cor mais comuns feitos em sistemas de design. Confira o exemplo na introdução deste artigo, se você não o encontrou, ele descreve o espaço do problema muito bem.

Ajustar a opacidade por um valor

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

Ajustar a opacidade para um valor específico

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

Inverter uma cor

A inversão de cores é uma função de ajuste de cor comum encontrada nas bibliotecas de cores. Uma maneira de fazer isso é converter uma cor em RGB e subtrair o valor de cada canal de 1.

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

Complementar uma cor

Se sua meta não era inverter uma cor, mas sim complementá-la, a rotação de matiz é provavelmente o que você está procurando. Escolha um espaço de cores que ofereça a cor como um ângulo e use calc() para girar a cor pelo valor que você quiser. Para encontrar o complemento de uma cor, gire a roda de cores em meia volta. Nesse caso, é possível adicionar ou subtrair do canal h por 180 para alcançar o resultado.

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

Contrastar uma cor

Como um método para alcançar proporções de contraste de cores acessíveis, considere L&midast; (Lstar). Isso usa o canal de luminosidade (L) perceptualmente uniforme (aproximadamente) de LCH e OKLCH, em um calc(). Dependendo se você está segmentando contraste baixo, médio ou alto, a delta de L&midast; é de aproximadamente 40, 50 ou 60.

Essa técnica funciona bem em qualquer matiz em LCH ou OKLCH.

Contrastar uma cor mais escura

A classe .well-contrasting-darker-color demonstra L* com um delta de 60. Como a cor de origem é escura (valor de luminosidade baixo), 60% (0,6) é adicionado ao canal de luminosidade. Essa técnica é usada para encontrar uma cor de texto escura bem contrastante com a mesma matiz em um plano de fundo claro.

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

Contrastar uma cor mais clara

A classe .well-contrasting-lighter-color também demonstra L* com um delta de 60%. Como a cor de origem é clara (valor alto de luminosidade), 0,60 é subtraído do canal de luminosidade.

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

Paletas de cores

A sintaxe de cores relativa é muito boa para criar paletas de cores. Ela é especialmente útil e poderosa devido ao número de espaços de cores disponíveis. Os exemplos a seguir usam o OKLCH porque o canal de luminosidade é confiável e o canal de matiz pode ser girado sem efeitos colaterais. O exemplo final demonstra uma combinação de ajustes de rotação de luminosidade e matiz para um resultado mais interessante.

Abra o código-fonte de exemplo para essas paletas e tente mudar o --base-color para ver como elas são dinâmicas. É divertido!

Se você gosta de vídeos, confira informações detalhadas sobre como criar paletas de cores no CSS com OKLCH no YouTube.

Paletas monocromáticas

Criar uma paleta monocromática é criar uma paleta com a mesma tonalidade, mas com variações de claridade e escuridão. A cor do meio é a cor de origem da paleta, em que duas variantes mais claras e duas mais escuras são colocadas em 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 */
}
Teste várias paletas criadas com sintaxe de cores relativa e OKLCH

O Open Props, uma biblioteca de variáveis CSS sem custo financeiro, oferece paletas de cores criadas com essa estratégia e as torna facilmente utilizáveis com uma importação. Eles também são criados com uma cor que pode ser personalizada. Basta fornecer uma cor para gerar uma paleta.

Paletas análogas

Como a rotação de matiz é muito fácil com OKLCH e HSL, é muito simples criar uma paleta de cores análogas. Gire a matiz em uma quantidade que você goste dos resultados e mude a cor de base. Observe as novas paletas sendo criadas pelo navegador.

: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 triádicas

Semelhantes às cores complementares, as paletas de cores triádicas são rotações de matiz opostas, mas harmoniosas, dadas uma cor de base. Quando uma cor complementar está no lado oposto de uma cor, como uma linha reta desenhada no meio da roda de cores, as paletas triádicas são como um triângulo de linhas, encontrando duas cores igualmente giradas a partir de uma cor base. Para fazer isso, gire a matiz 120deg.

Essa é uma simplificação da teoria das cores, mas é suficiente para você começar a usar as paletas triádicas mais complexas, se quiser.

: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 tetradicas

As paletas tetradísticas são quatro cores divididas igualmente ao redor da roda de cores, criando uma paleta sem um valor dominante claro. Você também pode pensar nisso como dois pares de cores complementares. Se usado com sabedoria, pode ser muito significativo.

Essa é uma simplificação da teoria das cores, mas é suficiente para você começar a usar as paletas tetradas mais complexas, se quiser.

: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 com uma ligeira rotação de matiz

Muitos especialistas em cores guardam esse truque na manga. O problema é que uma escala de cores monocromática pode ser bastante chata. A solução é adicionar uma rotação de matiz menor ou maior a cada nova cor à medida que a luminosidade é alterada.

O exemplo a seguir diminui a luminosidade em 10% em cada amostra e também gira a matiz em 10 graus. O resultado é uma paleta de hot pink a índigo que parece se misturar perfeitamente, como um 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));
}
Teste este placar criado com OKLCH e rotação de matiz

A interface da tabela de classificação a seguir usa essa estratégia de rotação de matiz. Cada item da lista rastreia o índice no documento como uma variável chamada --i. Esse índice é usado para ajustar a croma, a luminosidade e a matiz. O ajuste é de apenas 5% ou 5 graus, muito mais sutil do que o exemplo acima com deeppink. Portanto, é preciso ter bom senso para notar por que essa tabela de classificação pode estar em qualquer matiz com tanta elegância.

Mude a matiz no controle deslizante abaixo da tabela de classificação e veja a sintaxe de cores relativa criar momentos de cores incríveis.

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))
  );
}