Sintaxe de cores relativa de CSS

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

Adam Argyle
Adam Argyle

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ídeos, quase todos os artigos a seguir são abordados neste desafio de GUI.

Visão geral da sintaxe

O objetivo da sintaxe de cores 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 recolocadas na cor inalterada, 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 vista como 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 usar 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 é capacitadora, já que a escolha de um espaço de cores tende a se concentrar mais 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 ficará muito mais claro nas seções que demonstram casos de uso, já que espaços de cor diferentes se destacam em tarefas distintas.

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, conhecer 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! A tonalidade foi duplicada, pegando uma tonalidade de 120 e a transformando em 240, mudando 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 para usar.

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 abaixo têm muitas sintaxes alternativas para conseguir 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.

Clarear uma cor

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

Diminuir bastante

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

Clarear 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 que clareiam uma cor também são ótimos para escurecer.

Escurecer por um valor

O exemplo a seguir .darken-by-25 usa a cor azul e a converte em OKLCH e, em seguida, escurece o azul diminuindo o canal l (iluminação) em 25% multiplicando o valor por .75. Isso empurra a cor azul para o 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 é aumentada 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. Em primeiro lugar, é uma mudança de chroma, e não de saturation, porque os espaços de cores que podem aumentar o alcance dinâmico não usam saturação. Os espaços de cor que apresentam chroma são capazes de alto alcance dinâmico, permitindo que os autores aumentem a vibração de cores ainda mais do que a saturação.

.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 comum de ajuste de cores encontrada em bibliotecas de cores. Uma maneira de fazer isso é converter uma cor em RGB e, em seguida, 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 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 quiser. A busca do complemento de uma cor é feita girando metade de uma 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.

(em inglês)

Essa técnica funciona bem com qualquer tonalidade 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 é uma cor clara (brilho de alto valor), 0,60 é subtraído do canal de brilho.

.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 claridade e rotação de matiz para gerar 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 é fazer uma paleta da 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 todos criados com uma cor que você pode personalizar: basta dar uma cor e gerar uma paleta.

Paletas análogas

Como a rotação de matiz é tão fácil com OKLCH e HSL, é trivial criar uma paleta de cores análoga. 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 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 Tetradic

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 pequena simplificação da teoria das cores, mas é o suficiente para começar a usar paletas de cores mais complexas, se tiver interesse.

: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ático com uma leve 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 cores quentes 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 o motivo pelo qual 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))
  );
}