Executando verificação de segurança...
6

Entendendo Gradientes de Cores em CSS para Front-end e Visualização de Dados

T.L.;D.R.: Afinal, o que é um gradiente de cores? “Gradiente de cores é um conjunto de cores dispostas em uma ordem linear” (WIKIPEDIA CONTRIBUTORS, 2023a). E no contexto de um navegador web, há sintaxe CSS para declarar gradientes (MDN CONTRIBUTORS, 2023a). Mas para começar a dominá-los, devemos dar uma olhada sob o capô. E é isso que este artigo visa.

Originalmente publicado por mim em inglês no Medium.com: Understanding CSS Color Gradients for the Front-end and Data Visualization. Se você quiser saber mais sobre mim, me adicione no LinkedIn :)

O problema com a interpolação linear de cores em sRGB

Com CSS pode-se declarar várias formas de gradientes, como linear-gradient() — uma função de cor usada para pintar uma área em uma direção específica ao longo de uma linha reta (MDN CONTRIBUTORS, 2023b). Uma maneira direta de visualizar o que acontece entre duas paradas de cor em um gradiente é usar esta função para desenhar um gradiente com uma cor à esquerda e outra à direita. O exemplo abaixo declara isso usando a função de cor rgb() (MDN CONTRIBUTORS, 2023c).

.my-gradient {
    background-image: linear-gradient(
        to right,
        rgb(255, 255, 0),
        rgb(0, 0, 255)
    );
}


Amarelo para azul em gradiente linear sRGB.

Assim como a especificação do Módulo de Imagens CSS Nível 4 reconheceu, há cinza no meio (ATKINS JR. et al., 2023a, capítulo 3.1.1 exemplo 19). Então, por que isso? Veja bem, os gradientes CSS dependem da interpolação de cores. Isso acontece primeiro convertendo cada cor para um espaço de cores de interpolação, como sRGB (se nenhum for especificado) e, em seguida, interpolando cada componente (R, G e B) individualmente (ATKINS JR. et al., 2023a). Se você tiver amarelo rgb(255, 255, 0) de um lado e azul rgb(0, 0, 255) do outro, no centro está sua média aritmética: cinza rgb(128, 128, 128 ). É matematicamente correto e dentro da especificação, mas não perceptualmente preciso, pois parece que há uma terceira cor em um gradiente de duas cores. Isso pode ser corrigido adicionando mais cores no meio, removendo assim aquela área acinzentada até que pareça tão suave quanto se você a tivesse desenhado em um espaço de cor mais uniforme. Essa árdua tarefa resultaria em algo parecido com o exemplo abaixo.


Amarelo para azul em gradiente linear Oklab.

Existem soluções melhores do que adicionar cores manualmente para suavizar o gradiente, e irei analisá-las. Mas primeiro, vamos verificar por que essa não é uma ideia tão boa em primeiro lugar.

Comprando uma cor

Escolher uma cor e ajustá-la é uma tarefa mundana. Um desenvolvedor pode realizá-la diversas vezes ao dia. Pode até escolher cores programaticamente. Mas, muitas vezes, essa tarefa começa com a escolha de cores manualmente, usando o seletor de cores. Pense nessa ferramenta (o seletor de cores) como uma loja de departamentos; e sinta essa tarefa (escolher cores) como fazer compras nessa loja.

Para encontrar o produto que deseja, você deve navegar pelas fileiras de corredores até encontrar o corredor que deseja. Então você entra nesse corredor e caminha ao longo dele até encontrar a gôndola certa. Você olha para cima e para baixo naquela gôndola, encontra o produto desejado e o pega da prateleira. Ou, em nossa analogia, escolha a cor que deseja.


Seletor de cores Sliders RGB.

Um seletor de cores tem cores ordenadas em várias dimensões, assim como os produtos na loja. E o que essas dimensões representam depende do modelo de cores. Nós estamos vindo de um linear-gradient() usando rgb(), então já usamos o modelo RGB. Esse é um espaço cartesiano tridimensional no qual cada eixo representa a intensidade de uma luz colorida: Vermelho, Verde e Azul. Quando cada componente de cor está no máximo, você obtém o branco. No 0 absoluto, há preto.


Cubo RGB em Three.js.

Mas, como você deve ter percebido, existem maneiras mais intuitivas de procurar cores do que inserir valores de vermelho, verde e azul em campos. Digamos que você tenha um botão e queira deixar sua borda um pouco mais escura. Encontrar o brilho adequado geralmente significa mexer com a quantidade de todas essas cores primárias simultaneamente, tomando muito cuidado para não alterar a tonalidade da cor (matiz).

Essa é uma das razões pelas quais você pode encontrar outro tipo de modelo de cores no CSS [^1] e no próprio seletor de cores. O modelo de cores HSL: onde H significa Hue (matiz), expresso como um ângulo; S para Saturação, expressando quão cinza ou intensa é uma cor; e L para Luminosidade, quão escura ou brilhante é uma cor.


Seletor de cores Sliders HSL.


Bicone HSL em Three.js.

Bem melhor. Fazer pequenos ajustes agora é mais gerenciável, pois você pode tornar as cores mais acinzentadas, mais claras ou mais escuras sem alterar o componente de matiz. Mas, ao usar o HSL, você encontrará outro aborrecimento, que pode se manifestar da seguinte maneira. Digamos que você esteja construindo uma paleta de cores que usa amarelo e azul, assim como naquele gradiente com o qual começamos. Então você coloca o matiz, maximiza a saturação e insere o mesmo valor de luminosidade. Você acaba com um amarelo que machuca os olhos e um azul calmo. Para deixar os dois com a mesma intensidade, você diminui a claridade do amarelo, resultando em algo assim:

Isso ocorre porque rgb(), hsl(), cores nomeadas e códigos HEX são modelos de cores diferentes (eles apresentam maneiras diferentes de navegar pelas cores). Mas todos navegam pelo mesmo espaço de cores sRGB e estão todos vinculados às suas características (MDN CONTRIBUTORS, 2023e).

Características como ter primárias vermelho, verde e azul e ser codificado com função de transferência de luminosidade gamma — uma codificação herdada de monitores CRT que ajuda as cores a parecerem perceptualmente mais precisas (WIKIPEDIA CONTRIBUTORS, 2023b); mas também torna a aritmética mais difícil, porque a mesma expressão aplicada em diferentes intensidades de luz produzirá resultados irregulares (MOULIN, 2018).


Espaço de cores gamma.(MOULIN, 2018)

Depois, há o fato de que o sRGB representa todos os comprimentos de onda de luz (percebidos pelo olho como matiz) com a mesma intensidade. E isso é ótimo para física e colorimetria. Mas os olhos humanos não percebem o brilho uniformemente entre diferente tons, o que não é fácil mapear. Björn Ottosson (2020) criou a visualização abaixo. Nela ele compara um espaço de cores perceptualmente mais uniforme (Oklab), que ele mesmo projetou, com o espaço sRGB; alterando os matizes e mantendo os mesmos valores de saturação e luminosidade. Em seguida, ele demonstra como os matizes sRGB não se traduzem em luminosidade uniforme, convertendo a imagem sRGB para preto e branco no espaço de cores Oklab.


sRGB, Oklab, e sRGB convertido para tons de cinza em Oklab.

Lembra daquela metáfora que usamos sobre escolher uma cor ser como navegar em uma loja de departamentos? Bem, um gradiente é como escolher duas ou mais cores e caminhar entre elas: se o layout da loja (como as cores são organizadas no espaço) muda, então o caminho entre dois produtos (as cores) também muda.

Felizmente, o Módulo de Imagens CSS Nível 4 aceita declaração e interpolação em espaços de cores diferentes de sRGB (ATKINS JR. et al., 2023a), até defendendo o Oklab. Mas antes de falar mais sobre espaços de cores em CSS, vamos falar sobre espaços de cores.

O ano é 1931…

Ernest Lawrence inventa o ciclotron. A Porsche é fundada. Mahatma Gandhi é libertado da prisão. E a Comissão Internacional de Iluminação cria o espaço de cores CIE 1931 XYZ (WIKIPEDIA CONTRIBUTORS, 2023c, 2023d). Como Abraham (2019) colocou, o CIE 1931 não visa dizer como os humanos percebem as cores. O objetivo é criar um sistema no qual as cores possam ser medidas numericamente, expressadas, e reproduzidas em mídia impressa ou telas, assim como nas telas que mostram cores cuidadosamente programadas com CSS.

Como um sistema de correspondência de cores, o CIE XYZ representa os comprimentos de onda de luz que um ser humano pode ver[^2] e é baseado na física, tornando-o independente de dispositivos. É por isso que é usado como padrão para perfilar as cores que um dispositivo pode reproduzir. E também funciona ao contrário: estabelecendo parâmetros de fabricação de dispositivos para indústria, aos quais produtos devem atender e ser medidos contra, como o sRGB.


Cores sRGB representadas dentro do espaço de cor XYZ D65.

Mas medir comprimentos de onda para correspondência e reprodução de cores precisas é uma coisa; ser perceptualmente uniforme é outra completamente diferente. E para isso, a CIE propôs o espaço de cores L*a*b*, no qual L significa Luminosidade percebida; a: quão verde/vermelha é uma cor; e b: quão azul/amarela é uma cor. Esta mudança nos próprios componentes por si já garante um espaço de cores perceptivamente mais uniforme que o CIE XYZ (MDN CONTRIBUTORS, 2023f).


Esfera L*a*b* em Three.js.

Embora L*a*b* seja perceptualmente mais preciso do que CIE XYZ, o Oklab de Björn Ottosson é ainda mais, pois incorpora mecanismos para melhorar a aritmética e a uniformidade perceptual. Como o próprio Ottosson escreveu, é “[…] um novo espaço de cores perceptual, projetado para ser simples de usar, ao mesmo tempo em que faz um bom trabalho na previsão de luminosidade, croma e matiz percebidos” (OTTOSSON, 2020).

Agora, vamos plotar todas as cores sRGB dentro do espaço de cores do Oklab, visualizando sua organização em relação à percepção humana. E o sólido resultante está longe de ser regular, distanciando-se do cubo rgb(), do bicone hsl(), ou de seu primo cilíndrico, HSV (mais comumente usado em aplicativos de edição de fotos).


Cores sRGB representadas dentro do espaço de cor Oklab.


Espaço de cores uniforme.

De volta ao problema de interpolação de cores em diferentes espaços de cores

Com uma compreensão mais profunda sobre espaços de cores e modelos de cores, estamos preparados para explicar por que alguns outros fenômenos ocorrem ao interpolar cores sRGB. Um desses fenômenos é o desvio de matiz para roxo em gradientes de branco para azul, como o abaixo.


Branco para azul em gradiente linear sRGB.

Esse é outro efeito colateral de misturar primários sRGB de maneira linear, mas não perceptualmente uniforme. O caminho do branco ao azul é uma linha reta, mas visivelmente cruza tons de roxo. Infelizmente, este problema específico também afeta lab() e sua representação cilíndrica, lch() (MDN CONTRIBUTORS 2023g). Felizmente, existem funções CSS oklab() e oklch() (MDN CONTRIBUTORS 2023i), assim como exploradas por Sitnik e Turner (2022) em “OKLCH in CSS: why we moved from RGB and HSL” .


“Uma fatia de tonalidade constante de espaços LCH e OKLCH com a mesma tonalidade. A fatia LCH é azul de um lado e roxa do outro. OKLCH mantém uma tonalidade constante conforme o esperado.” (SITNIK & TURNER, 2022).

E, como esperado, se você desenhar o mesmo gradiente interpolando as cores dentro do Oklab, o desvio de matiz desaparecerá [^3].


Branco para azul e gradient linear Oklab.

Mas há um problema e, se você não estiver usando o navegador Safari, talvez já tenha detectado. Até março de 2023, apenas o Safari implementou a função color() do Módulo de Cores CSS Nível 4 entre outros recursos nos quais as demos nesta página dependem (CONTRIBUTORS, 2023). Mas, graças ao Color.js, uma biblioteca escrita pelos contribuidores do W3C Lea Verou e Chris Lilley (2023), pude fazer aproximações com força bruta, que são executadas automaticamente se o browser não oferecer suporte aos recursos necessários. Esta abordagem foi inspirada principalmente no artigo de Comeau (2022) “Make Beautiful Gradients”.

Obrigado por ler este artigo. Se você acha que deixei alguma coisa escapar ou deseja discutir este tópico, visite alexandrelopes.design, onde você pode encontrar minhas informações de contato atualizadas.

Mais uma coisa

Os navegadores sempre seguirão o caminho mais curto ao interpolar matiz em modelos de cores como HSL, onde o matiz é expresso como um arco. Mas o Módulo de Cores CSS Nível 4 permite a escolha de outros caminhos de interpolação, como o mais longo, crescente ou decrescente (ATKINS JR. et al., 2023b, chap. 12). Isso permitirá que desenvolvedores web tragam novos tipos de gradientes, como o abaixo. Adam Argyle (2022) explora esse tópico com ainda mais exemplos, caso você tenha curiosidade.


Vermelho para vermelho em gradiente linear Oklab pelo maior caminho de matiz.

Referências

ABRAHAM, Chandler. A beginner’s guide to (CIE) colorimetry. [S. l.: s. n.], feb. 2019. Disponível em: https://medium.com/hipster-color-science/a-beginners-guide-to-colorimetry-401f1830b65a. Acesso em: 1 mar. 2023.

ARGYLE, Adam. Gradient hue interpolation. [S. l.: s. n.], dec. 2022. Disponível em: https://nerdy.dev/gradients-going-the-shorter-longer-increasing-or-decreasing-route. Acesso em: 1 mar. 2023.

ATKINS JR., Tab; J. ETEMAD, Elika; VEROU, Lea. CSS Images Module Level 4. [S. l.: s. n.], feb. 2023a. Disponível em: https://www.w3.org/TR/css-images-4/. Acesso em: 1 mar. 2023.

ATKINS JR., Tab; LILLEY, Chris; VEROU, Lea. CSS Color Module Level 4. [S. l.: s. n.], feb. 2023b. Disponível em: https://www.w3.org/TR/css-color-4/. Acesso em: 1 mar. 2023.

COMEAU, Josh W. Make beautiful gradients. [S. l.: s. n.], jan. 2022. Disponível em: https://www.joshwcomeau.com/css/make-beautiful-gradients/. Acesso em: 1 mar. 2023.

CONTRIBUTORS, Can I. Use. Feature: CSS color() function. [S. l.: s. n.], feb. 2023. Disponível em: https://caniuse.com/css-color-function. Acesso em: 1 mar. 2023.

MDN CONTRIBUTORS. <Gradient>. [S. l.: s. n.], feb. 2023a. Disponível em: https://developer.mozilla.org/en-US/docs/Web/CSS/gradient. Acesso em: 1 mar. 2023.

MDN CONTRIBUTORS. Hsl(). [S. l.: s. n.], feb. 2023d. Disponível em: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl. Acesso em: 1 mar. 2023.

MDN CONTRIBUTORS. Lab(). [S. l.: s. n.], feb. 2023f. Disponível em: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/lab. Acesso em: 1 mar. 2023.

MDN CONTRIBUTORS. Lch(). [S. l.: s. n.], feb. 2023g. Disponível em: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/lch. Acesso em: 1 mar. 2023.

MDN CONTRIBUTORS. Linear-gradient(). [S. l.: s. n.], feb. 2023b. Disponível em: https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient. Acesso em: 1 mar. 2023.

MDN CONTRIBUTORS. Oklab(). [S. l.: s. n.], feb. 2023h. Disponível em: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklab. Acesso em: 1 mar. 2023.

MDN CONTRIBUTORS. Oklch(). [S. l.: s. n.], feb. 2023i. Disponível em: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklch.

MDN CONTRIBUTORS. RGB. [S. l.: s. n.], feb. 2023e. Disponível em: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/rgb. Acesso em: 1 mar. 2023.

MDN CONTRIBUTORS. Rgb(). [S. l.: s. n.], feb. 2023c. Disponível em: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/rgb. Acesso em: 1 mar. 2023.

MOULIN, Matthias. Linear, gamma and sRGB color spaces. [S. l.: s. n.], jul. 2018. Disponível em: https://matt77hias.github.io/blog/2018/07/01/linear-gamma-and-sRGB-color-spaces.html. Acesso em: 3 mar. 2023.

OTTOSSON, Björn. A perceptual color space for image processing. [S. l.: s. n.], dec. 2020. Disponível em: https://bottosson.github.io/posts/oklab/. Acesso em: 1 mar. 2023.

SITNIK, Andrey; TURNER, Travis. OKLCH in CSS: Why we moved from RGB and HSL. [S. l.: s. n.], oct. 2022. Disponível em: https://evilmartians.com/chronicles/oklch-in-css-why-quit-rgb-hsl#oklch-vs-oklab--lch-vs-lab. Acesso em: 1 mar. 2023.

VEROU, Lea; LILLEY, Chris. Color.js. [S. l.: s. n.], mar. 2023. Disponível em: https://colorjs.io. Acesso em: 1 mar. 2023.

WIKIPEDIA CONTRIBUTORS. Color gradient. [S. l.: s. n.], jan. 2023a. Page Version ID: 1135775894. Disponível em: https://en.wikipedia.org/w/index.php?title=Color_gradient&oldid=1135775894. Acesso em: 1 mar. 2023.

WIKIPEDIA CONTRIBUTORS. 1931. [S. l.: s. n.], feb. 2023c. Page Version ID: 1141750744. Disponível em: https://en.wikipedia.org/w/index.php?title=1931&oldid=1141750744. Acesso em: 1 mar. 2023.

WIKIPEDIA CONTRIBUTORS. CIE 1931 color space. [S. l.: s. n.], feb. 2023d. Page Version ID: 1141092007. Disponível em: https://en.wikipedia.org/w/index.php?title=CIE_1931_color_space&oldid=1141092007. Acesso em: 1 mar. 2023.

WIKIPEDIA CONTRIBUTORS. sRGB. [S. l.: s. n.], feb. 2023b. Page Version ID: 1137144175. Disponível em: https://en.wikipedia.org/wiki/SRGB. Acesso em: 3 mar. 2023.

[^1] Para declarar cores usando o modelo de cores HSL em CSS, use a função de cores hsl()(MDN CONTRIBUTORS, 2023d).

[^2] Ou pelo menos os humanos envolvidos nos experimentos feitos durante a década de 1920 que foram usados como base para o CIE XYZ.

[^3] observe que o gradiente começa com cores que se encaixam perfeitamente no espaço de cores sRGB: branco hsl(0, 0, 100) e azul hsl(240, 100, 50). Mas como seu caminho no espaço de cores do Oklab vai além da superfície sRGB, alguns de seus valores vão além de 100% quando convertidos de volta para hsl().

Carregando publicação patrocinada...