Uma visão geral básica de como estabelecer um esquema de cores dinâmico e configurável
Neste post, quero compartilhar ideias sobre como gerenciar vários esquemas de cores no CSS. Teste a demonstração.
Se preferir vídeos, confira a versão desta postagem no YouTube:
Visão geral
Vamos criar um sistema de cores acessível com propriedades personalizadas e calc()
para
criar uma página da Web que se adapte às preferências do usuário, mantendo a experiência
de criação mínima. Começamos com uma cor de marca-base e criamos um sistema de
variantes a partir dela: duas cores de texto, quatro cores de superfície e uma sombra correspondente.
Este guia começa definindo todas as cores de cada esquema de cores. Só no final é que eles são usados para mudar a página.
A marca
Muitas vezes, a cor da marca já foi estabelecida e é enviada como
hexadecimal ou
RGB. Este desafio de GUI
tem uma cor de marca de base #0af
. Primeiro, para esse sistema de cores, o valor hexadecimal
precisa ser convertido em
hsl.
* {
--brand: #0af;
--brand: hsl(200 100% 50%);
}
Para ativar um conceito de escurecimento ou clareamento da cor da marca, digamos 20%, os três canais do valor de cor hsl precisam ser extraídos nas próprias propriedades personalizadas, como esta:
* {
--brand-hue: 200;
--brand-saturation: 100%;
--brand-lightness: 50%;
}
O CSS pode fazer cálculos nessas propriedades de cor, por exemplo, calc(var(--brand-lightness) -
20%)
para diminuir o valor de luminosidade em 20%. Isso é fundamental para criar um
esquema de cores, já que o CSS pode manter todas as cores na mesma família de matiz ajustando a
saturação e a luminosidade do HSL.
Tema claro
Cada variante de cor será marcada com o esquema correspondente. Neste caso, cada
é anexado com -light
.
Marca
Começando pela cor da marca, ela é recriada envolvendo as propriedades personalizadas --brand-hue
, --brand-saturation
e --brand-lightness
dentro dos parênteses da função hsl ()
,
sem cálculos:
* {
--brand-light: hsl(var(--brand-hue) var(--brand-saturation) var(--brand-lightness));
}
Cores do texto
Em seguida, os elementos essenciais de um esquema de cores precisam de cores de texto. Em um tema claro, o texto precisa ser muito escuro. Observe como a luminosidade das cores a seguir é baixa, bem abaixo de 50%.
* {
--text1-light: hsl(var(--brand-hue) var(--brand-saturation) 10%);
--text2-light: hsl(var(--brand-hue) 30% 30%);
}
--text1-light
, por ser muito escuro com 10% de luminosidade, mantém a saturação de 100%
para que a cor da marca ainda apareça no azul-marinho escuro.
--text2-light
, não é tão escura quanto a primeira cor, o que é bom, já que é
uma cor secundária e também é muito menos saturada.
Cores da superfície
As cores da superfície são os planos de fundo, bordas e outras superfícies decorativas em que o texto fica ou em que está inserido. Em um tema claro, essas são as cores claras, em contraste com as cores de texto que eram escuras. Para criar cores claras com hsl, usaremos valores de porcentagem mais altos no terceiro valor de luminosidade. Também vamos diminuir a saturação para que os cinzas claros não fiquem muito saturados.
* {
--surface1-light: hsl(var(--brand-hue) 25% 90%);
--surface2-light: hsl(var(--brand-hue) 20% 99%);
--surface3-light: hsl(var(--brand-hue) 20% 92%);
--surface4-light: hsl(var(--brand-hue) 20% 85%);
}
Quatro cores de superfície foram criadas, já que as cores decorativas tendem a precisar de mais
variantes para momentos interativos, como :focus
ou :hover
, ou para criar a
aparência de camadas de papel. Nesses cenários, é bom fazer a transição
de --surface2-light
ao passar o cursor para --surface3-light
, para que o passar o cursor resulte em um
aumento de contraste (99% de claridade para 92% de claridade, tornando-o mais escuro).
Sombras
As sombras em um esquema de cores são mais importantes, mas adicionam uma natureza realista ao efeito e ajudam a diferenciá-lo de sombras realistas com base preta. Para fazer isso, a cor da sombra vai usar a propriedade personalizada de matiz, ser levemente saturada com o matiz, mas ainda muito escura. Basicamente, criando uma sombra ligeiramente azul e muito escura.
* {
--surface-shadow-light: var(--brand-hue) 10% 20%;
--shadow-strength-light: .02;
}
--surface-shadow-light
não está encapsulado em uma função hsl. Isso ocorre porque o
valor --shadow-strength
será combinado para criar alguma opacidade, e o CSS precisa
das partes para realizar cálculos. Pule para a seção
"Sombra de raio" para saber mais.
Cores claras todas juntas
Não é preciso procurar para descobrir como as cores claras são feitas. Elas estão todas em um só lugar no CSS.
* {
--brand-light: hsl(var(--brand-hue) var(--brand-saturation) var(--brand-lightness));
--text1-light: hsl(var(--brand-hue) var(--brand-saturation) 10%);
--text2-light: hsl(var(--brand-hue) 30% 30%);
--surface1-light: hsl(var(--brand-hue) 25% 90%);
--surface2-light: hsl(var(--brand-hue) 20% 99%);
--surface3-light: hsl(var(--brand-hue) 20% 92%);
--surface4-light: hsl(var(--brand-hue) 20% 85%);
--surface-shadow-light: var(--brand-hue) 10% calc(var(--brand-lightness) / 5);
--shadow-strength-light: .02;
}
Tema escuro
A maioria das marcas não começa com um tema escuro, é uma variante do tema principal, geralmente mais claro. Os usuários, por outro lado, geralmente escolhem um tema escuro para diferentes contextos, como à noite. Esses fatores me levaram a considerar dois aspectos com temas escuros:
- Os usuários geralmente estarão no escuro ao usar esse tema. Portanto, teste no escuro.
- As cores precisam ser desaturadas para não vibrar na tela por serem muito intensas.
Marca
O tema claro usou os três valores dos canais de cor hsl da marca sem alterações, mas o tema escuro não. A saturação é cortada pela metade, e a luminosidade é reduzida em 50%.
* {
--brand-dark: hsl(
var(--brand-hue)
calc(var(--brand-saturation) / 2)
calc(var(--brand-lightness) / 1.5)
);
}
Cores do texto
Em um tema escuro, as cores do texto precisam ser claras. As cores a seguir têm valores altos de luminosidade, aproximando-as do branco.
* {
--text1-dark: hsl(var(--brand-hue) 15% 85%);
--text2-dark: hsl(var(--brand-hue) 5% 65%);
}
Cores da superfície
Em um tema escuro, as cores da superfície precisam ser escuras. As cores a seguir têm luminosidade e saturação baixas, com a 1ª superfície sendo a mais escura, com 10%.
* {
--surface1-dark: hsl(var(--brand-hue) 10% 10%);
--surface2-dark: hsl(var(--brand-hue) 10% 15%);
--surface3-dark: hsl(var(--brand-hue) 5% 20%);
--surface4-dark: hsl(var(--brand-hue) 5% 25%);
}
Sombras
Em um tema escuro, as sombras podem ser muito difíceis de ver. Faz sentido, já que é difícil
escurecer algo que já está bastante escuro. É aqui que
--shadow-strength-dark
é muito útil, porque permite escurecer as
sombras mudando uma variável.
* {
--surface-shadow-dark: var(--brand-hue) 50% 3%;
--shadow-strength-dark: .8;
}
Além disso, observe a saturação dessa sombra. Você consegue notar a cor quando olha para a interface? Tente remover a saturação das ferramentas do desenvolvedor, qual você prefere?!
Cores escuras todas juntas
* {
--brand-dark: hsl(var(--brand-hue) calc(var(--brand-saturation) / 2) calc(var(--brand-lightness) / 1.5));
--text1-dark: hsl(var(--brand-hue) 15% 85%);
--text2-dark: hsl(var(--brand-hue) 5% 65%);
--surface1-dark: hsl(var(--brand-hue) 10% 10%);
--surface2-dark: hsl(var(--brand-hue) 10% 15%);
--surface3-dark: hsl(var(--brand-hue) 5% 20%);
--surface4-dark: hsl(var(--brand-hue) 5% 25%);
--surface-shadow-dark: var(--brand-hue) 50% 3%;
--shadow-strength-dark: .8;
}
Tema escuro
Esse esquema de cores é sobre orquestrar a luminosidade e a saturação. Deve haver saturação suficiente para que a matiz ainda seja visível, mas também é necessário passar por pontuações de contraste, já que o objetivo é ter um contraste fraco e baixo.
Marca
* {
--brand-dim: hsl(
var(--brand-hue)
calc(var(--brand-saturation) / 1.25)
calc(var(--brand-lightness) / 1.25)
);
}
Cores do texto
* {
--text1-dim: hsl(var(--brand-hue) 15% 75%);
--text2-dim: hsl(var(--brand-hue) 10% 61%);
}
Cores da superfície
* {
--surface1-dim: hsl(var(--brand-hue) 10% 20%);
--surface2-dim: hsl(var(--brand-hue) 10% 25%);
--surface3-dim: hsl(var(--brand-hue) 5% 30%);
--surface4-dim: hsl(var(--brand-hue) 5% 35%);
}
Sombras
* {
--surface-shadow-dim: var(--brand-hue) 30% 13%;
--shadow-strength-dim: .2;
}
Escurecer todas as cores
* {
--brand-dim: hsl(var(--brand-hue) calc(var(--brand-saturation) / 1.25) calc(var(--brand-lightness) / 1.25));
--text1-dim: hsl(var(--brand-hue) 15% 75%);
--text2-dim: hsl(var(--brand-hue) 10% 61%);
--surface1-dim: hsl(var(--brand-hue) 10% 20%);
--surface2-dim: hsl(var(--brand-hue) 10% 25%);
--surface3-dim: hsl(var(--brand-hue) 5% 30%);
--surface4-dim: hsl(var(--brand-hue) 5% 35%);
--surface-shadow-dim: var(--brand-hue) 30% 13%;
--shadow-strength-dim: .2;
}
Cores acessíveis
Observe como a menor luminosidade no conjunto de cores de texto escuro é de 65% e a maior luminosidade nas superfícies escuras é de 25%. Isso é 40% de espaço entre elas. No tema claro, há 55% de espaço para respirar no tema claro. Manter as diferenças de luminosidade entre o texto e as cores da superfície em cerca de 40 a 50% pode ajudar a manter as taxas de contraste de cores altas, além de ser uma variável sutil para ajustar caso as pontuações sejam baixas.
Eu chamo isso de "bump bump til ya pass", que é a interação de aumentar o valor de luminosidade até que uma ferramenta mostre que estou passando.
Cada um dos temas criados neste desafio passou nas pontuações de contraste. O esquema de cores escuras tem o menor contraste, mas ainda atende aos requisitos mínimos. Para ajudar outras pessoas da equipe a usar cores contrastantes, é uma boa ideia criar uma classe que combine uma cor de superfície com uma cor de texto acessível.
.surface1 {
background-color: var(--surface1);
color: var(--text2);
}
.surface2 {
background-color: var(--surface2);
color: var(--text2);
}
.surface3 {
background-color: var(--surface3);
color: var(--text1);
}
.surface4 {
background-color: var(--surface4);
color: var(--text1);
}
Sombra de Rad
Os temas usam uma classe utilitária chamada .rad-shadow
. Essa sombra foi gerada
na ferramenta Sombra suave, que eu adoro. Peguei o snippet gerado e o personalizei com minhas próprias cores e
cálculos de opacidade. O motivo disso foi criar uma sombra que pudesse ser ajustada
em cada esquema de cores.
Para isso, criei duas variáveis para cada esquema de cores, uma cor de sombra e uma intensidade de sombra. A cor é para ajustes de saturação e escuridão, enquanto a intensidade é uma maneira fácil de aumentar a intensidade da sombra quando se trata de um esquema de cores escuras. O resultado final foi mais ou menos assim.
:root {
--surface-shadow-light: var(--brand-hue) 10% 20%;
--shadow-strength-light: .02;
}
.rad-shadow {
box-shadow:
0 2.8px 2.2px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .03)),
0 6.7px 5.3px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .01)),
0 12.5px 10px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .02)),
0 22.3px 17.9px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .02)),
0 41.8px 33.4px hsl(var(--surface-shadow) / calc(var(--shadow-strength) + .03)),
0 100px 80px hsl(var(--surface-shadow) / var(--shadow-strength))
;
}
Se eu fosse mais longe com as sombras no meu esquema de cores, também faria com que os ângulos de sombra fossem um token de design constante, já que a direção da luz precisa ser a mesma entre todas as sombras do design.
Como usar os esquemas de cores
Com a pré-definição de cores concluída, é hora de transformá-las em propriedades independentes do esquema. Quero dizer que, como autor de CSS neste projeto de esquema de cores, raramente é necessário acessar o valor de um esquema de cores específico. Quero facilitar a manutenção do tema.
Para isso, o esquema de cores precisa ser usado exclusivamente pelas
propriedades personalizadas genéricas, que vamos definir em breve. Dessa forma,
as pessoas que usam as variáveis de design nunca precisam se preocupar com qual esquema de cores está
definido no momento, elas só precisam usar as cores da superfície e do texto. Em vez de
color: var(--text1-light)
, use color: var(--text1)
. Todas as adaptações e mudanças
de cores são feitas em um nível muito mais alto no CSS.
Os estilos de conexão do tema claro no bloco de código a seguir
conectam uma propriedade personalizada genérica com a cor específica do tema claro. Agora, todos
os usos de var(--brand)
vão usar a cor clara da marca.
Tema claro (automático)
:root {
color-scheme: light;
--brand: var(--brand-light);
--text1: var(--text1-light);
--text2: var(--text2-light);
--surface1: var(--surface1-light);
--surface2: var(--surface2-light);
--surface3: var(--surface3-light);
--surface4: var(--surface4-light);
--surface-shadow: var(--surface-shadow-light);
--shadow-strength: var(--shadow-strength-light);
}
O site agora está usando o tema claro. Este é um momento muito divertido e bem-sucedido! Vamos ter mais alguns desses momentos ao usar nossas cores predefinidas em outros contextos de esquemas de cores.
Tema escuro (automático)
@media (prefers-color-scheme: dark) {
:root {
color-scheme: dark;
--brand: var(--brand-dark);
--text1: var(--text1-dark);
--text2: var(--text2-dark);
--surface1: var(--surface1-dark);
--surface2: var(--surface2-dark);
--surface3: var(--surface3-dark);
--surface4: var(--surface4-dark);
--surface-shadow: var(--surface-shadow-dark);
--shadow-strength: var(--shadow-strength-dark);
}
}
Tema claro
[color-scheme="light"] {
color-scheme: light;
--brand: var(--brand-light);
--text1: var(--text1-light);
--text2: var(--text2-light);
--surface1: var(--surface1-light);
--surface2: var(--surface2-light);
--surface3: var(--surface3-light);
--surface4: var(--surface4-light);
--surface-shadow: var(--surface-shadow-light);
--shadow-strength: var(--shadow-strength-light);
}
Tema escuro
[color-scheme="dark"] {
color-scheme: dark;
--brand: var(--brand-dark);
--text1: var(--text1-dark);
--text2: var(--text2-dark);
--surface1: var(--surface1-dark);
--surface2: var(--surface2-dark);
--surface3: var(--surface3-dark);
--surface4: var(--surface4-dark);
--surface-shadow: var(--surface-shadow-dark);
--shadow-strength: var(--shadow-strength-dark);
}
Tema escuro
[color-scheme="dim"] {
color-scheme: dark;
--brand: var(--brand-dim);
--text1: var(--text1-dim);
--text2: var(--text2-dim);
--surface1: var(--surface1-dim);
--surface2: var(--surface2-dim);
--surface3: var(--surface3-dim);
--surface4: var(--surface4-dim);
--surface-shadow: var(--surface-shadow-dim);
--shadow-strength: var(--shadow-strength-dim);
}
Nesse ponto, os autores podem usar os esquemas de cores genéricos fornecidos conforme necessário e não precisam se preocupar com temas novamente.
Conclusão
Agora que você sabe como eu fiz, como você faria? 🙂
Vamos diversificar nossas abordagens e aprender todas as maneiras de criar na Web. Crie um Codepen ou hospede sua própria demonstração, envie um tweet para mim e vou adicionar à seção "Remixes da comunidade" abaixo.
Origem
Remixes da comunidade
- @chris-kruining adicionou um controle deslizante de matiz,
cores de status e modos de contraste para no-preference
, more
e less
:
demonstração.