Sintaxe de cores relativa de CSS

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

Adam Argyle
Adam Argyle

No Chrome 119, há um recurso de cores 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 para autores e designers:

Para modificar a opacidade de uma cor, antes da sintaxe de cor relativa, é necessário criar propriedades personalizadas para os canais de uma cor, geralmente HSL, e juntá-los em uma cor final e na cor da variante final. Isso significa gerenciar muitas peças coloridas, o que pode se tornar rapidamente sobrecarregado.

: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, é possível criar uma cor de marca com qualquer espaço de cor ou sintaxe necessário e criar uma variante de meia opacidade com muito menos código. Também é muito mais fácil ler a intent 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 cor comuns.

Caso você prefira vídeo, quase todos os artigos a seguir são abordados 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 de base é chamada de cor de origem, que é a cor que vem após a nova palavra-chave from. O navegador converte e divide 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 superior verde e arqueando no início do rgb da função.
Essa seta se divide em quatro setas que apontam para a variável relevante delas. As quatro setas são vermelha, verde, azul e alfa. Vermelho e azul têm um valor 0, verde
é 128 e Alfa é 100%.

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

Essa imagem mostra o detalhamento, o processo e as variáveis, mas também não muda a cor. As variáveis são recolocadas na cor inalterada, resultando em uma cor verde imóvel.

A palavra-chave from

A primeira parte da sintaxe a ser aprendida é a adição da parte from <color> para especificar uma cor. Ela é inserida logo antes de os valores serem especificados. Veja 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 cor

Em termos mais simples, ele converte 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 as propriedades personalizadas e a sintaxe de cores relativa são uma ótima combinação, porque você pode eliminar o mistério da cor from. Geralmente, você também não precisa saber o formato da cor da propriedade personalizada, já que cria uma nova cor em um formato de sua escolha.

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

Trabalhe no espaço de cores que preferir

É possível escolher o espaço de cores com a notação de cor funcional.

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 depois de from é convertida no espaço de cores, conforme especificado no início da cor relativa. A entrada e a saída não precisam corresponder, o que é uma liberdade total.

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

Misturar, fazer a correspondência, omitir e repetir as variáveis

Algo estranho, mas interessante, sobre essa sintaxe: as variáveis não precisam ser colocadas em 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 variável

A sintaxe também fornece a opacidade como uma variável com o nome alpha. É opcional e vem depois de / na notação de cor 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% */

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

Até agora, criamos a cor verde repetidamente. Aprender a sintaxe e se familiarizar com as etapas de conversão e desestruturação. Agora é hora de modificar as variáveis, alterar a saída para que 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, assumindo uma tonalidade de 120 e a transformando em 240, mudando completamente a cor. Isso girou a tonalidade ao longo da roda de cores, um truque legal e simplificado muito simples com espaços de cores cilíndricos, como HSL, HWB, LCH e OKLCH.

Para conferir visualmente os valores dos canais e acertar os cálculos, sem adivinhar ou memorizar os detalhes, teste esta ferramenta de valores de sintaxe de canais relativos de cores. Ela 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 a compatibilidade com navegadores

@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 sintaxes alternativas para alcançar resultados semelhantes ou iguais. As variações vêm dos espaços de cores e dos canais que eles oferecem.

Além disso, muitos exemplos mostram ajustes de cor com o uso de by e to. Uma mudança de cor by é uma mudança de cor relativa, que usa o valor da variável e faz um ajuste com base no valor atual. Uma mudança de cor to é absoluta, ou seja, 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.

Clarear por uma quantidade

O exemplo a seguir .lighten-by-25 usa a cor blue e a converte em OKLCH, depois clareia o azul aumentando o canal l (iluminação) multiplicando o valor atual por 1.25. Isso faz com que o brilho azul se torne branco em 25%.

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

Clarear para um valor específico

O exemplo de .lighten-to-75 abaixo não usa o canal l para clarear blue. Em vez disso, ele substitui completamente o valor por 75%.

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

Escurecer uma cor

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

Escurecer um nível

O exemplo a seguir .darken-by-25 usa a cor azul e a converte em OKLCH, depois escurece o azul diminuindo o canal l (iluminação) em 25% multiplicando o valor por .75. Isso eleva 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 de .darken-to-25 abaixo não usa o canal l para escurecer blue. Em vez disso, ele substitui completamente o valor por 25%.

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

Saturar uma cor

Saturar por uma quantidade

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

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

Saturar até uma quantidade específica

O exemplo de .saturate-to-100 abaixo não usa o canal s de hsl(), ele especifica um valor de saturação desejado. Neste exemplo, a saturação aumenta para 100%.

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

Dessaturar uma cor

Dessaturar por uma quantidade

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

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

Dessaturar para um valor específico

Em vez de dessaturar um valor específico, você pode fazer isso até um valor específico. O exemplo abaixo, .desaturate-to-25, 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);
}
.

Otimização de chroma de uma cor

Esse efeito é semelhante à saturação de uma cor, mas é diferente de algumas maneiras. Em primeiro lugar, é uma mudança de chroma, não de saturation, porque os espaços de cor que podem ser aprimorados no alto intervalo dinâmico não usam a saturação. Os espaços de cor que apresentam chroma têm alta capacidade de alcance dinâmico, permitindo que os autores intensifiquem a vibração das cores além da saturação.

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

Ajustar a opacidade de uma cor

Fazer uma variante semitransparente de uma cor é um dos ajustes de cor mais comuns feitos em sistemas de design. Se você não viu o exemplo na introdução deste artigo, ele descreve muito bem a questão do problema.

Ajustar a opacidade com 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 comum encontrada em bibliotecas de cores. Uma maneira de fazer isso é converter uma cor para 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 o objetivo 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 matiz como um ângulo e use calc() para girar a tonalidade até o valor que você quiser. A descoberta do complemento de uma cor é feita girando-se meia volta. Nesse caso, é possível adicionar ou subtrair do canal h por 180 para chegar ao resultado.

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

Contrastar uma cor

Como método para obter taxas de contraste de cores acessíveis, considere L&midast; (Lstar). Isso usa o canal de iluminação perceptivamente uniforme (L) de LCH e OKLCH, em um calc(). Dependendo se você está segmentando baixo, médio ou alto contraste, o delta de L&midast; fica em torno de 40, 50 ou 60.

.

Esta técnica funciona bem em 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 (brilho de baixo valor), 60% (0,6) é adicionado ao canal de claridade. Essa técnica é usada para encontrar uma cor de texto escuro bem contrastante, de mesmo tom em um 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 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. Ele é especialmente útil e poderoso devido ao número de espaços de cor disponíveis. Todos os exemplos abaixo usam OKLCH, porque o canal de brilho é 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 gerar um resultado mais interessante.

Abra o código-fonte de exemplo e tente mudar o --base-color para conferir a dinâmica dessas paletas. É divertido!

Se você gostar do vídeo, vou fornecer 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 matiz, mas com variações de luminosidade 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 um dos lados.

: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 */
}
Experimente diversas paletas feitas com sintaxe de cor relativa e OKLCH

Open Props, uma biblioteca de variáveis CSS sem custo financeiro, oferece paletas de cores criadas com essa estratégia e facilita o uso com uma importação. Elas também são criadas com base em uma cor que você pode personalizar. Basta dar uma cor e ela cospe uma paleta.

.

Paletas análogas

Como a rotação de matizes é muito fácil com OKLCH e HSL, é trivial criar uma paleta de cores análoga. Gire a tonalidade até um valor que você goste dos resultados, mude a cor de base e veja novas paletas serem 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

Assim como as cores complementares, as paletas de cores triádicas são rotações de tons opostos, mas harmoniosos, dada uma cor de base. Onde 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 em relação a uma cor de base. Para fazer isso, gire a matiz 120deg.

Essa é uma pequena simplificação da teoria das cores, mas é o suficiente para começar a conhecer as paletas triádicas mais complexas, caso tenha interesse.

: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 tetrádica

As paletas trádica são quatro cores divididas uniformemente em torno da roda de cores, fazendo uma paleta sem um valor dominante claro. Você também pode pensar nisso, como dois pares de cores complementares. Se usada com sabedoria, ela pode ser muito significativa.

Essa é uma leve simplificação da teoria das cores, mas é suficiente para começar a usar as paletas tetrádicas mais complexas, caso tenha 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 mantêm esse truque na manga. O problema é que uma escala de cor monocromática pode ser bem entediante. A solução é adicionar uma rotação de matiz menor ou maior a cada nova cor conforme o brilho muda.

O exemplo a seguir diminui o brilho em 10% de cada amostra e também gira a matiz em 10 graus. O resultado é uma paleta de cores rosa e í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 esse placar criado com OKLCH e rotação de matiz

A interface de placar a seguir usa essa estratégia de rotação de matiz. Cada item da lista acompanha o índice no documento como uma variável chamada --i. Esse índice é usado para ajustar o chroma, o brilho e a matiz. O ajuste é de apenas 5% ou 5°, muito mais sutil do que o exemplo acima com cor-de-rosa. Por isso, é preciso observar por que o placar pode estar em qualquer tom com tanta elegância.

Mude a tonalidade no controle deslizante abaixo do placar e confira a sintaxe de cores relativa para criar belos momentos de cor.

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