Visão geral básica de como estabelecer um esquema de cores dinâmico e configurável
Nesta postagem, quero compartilhar ideias sobre maneiras de gerenciar vários esquemas de cores no CSS. Teste a demonstração.
Se preferir vídeo, aqui está uma versão do YouTube desta postagem:
Visão geral
Vamos criar um sistema de cores acessível com propriedades personalizadas e calc()
para
criar uma página da Web adaptável às preferências do usuário, mantendo mínima a experiência de
criação. Começamos com uma cor de marca de 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 com a definição de todas as cores de cada esquema de cores antecipadamente. Eles só são usados até o final para mudar a página.
A marca
Muitas vezes, uma cor de marca já foi estabelecida e é entregue como
hex ou
rgb. Esse desafio da GUI
tem uma cor de marca básica #0af
. Em primeiro lugar, 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 escurecer ou clarear a cor da marca, digamos 20%, os três canais do valor da cor hsl precisam ser extraídos para as próprias propriedades personalizadas, desta forma:
* {
--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 do brilho em 20%. Isso é fundamental para criar um
esquema de cores, já que o CSS pode manter todas as cores na mesma família de matizes ajustando as
quantidades de saturação e luminosidade do Hsl.
Tema claro
Cada variante de cor será marcada com o esquema correspondente. Nesse caso, cada uma
é anexada com -light
.
Marca
Começando pela cor da marca, ela é recriada ao combinar as propriedades personalizadas --brand-hue
, --brand-saturation
e --brand-lightness
dentro do parêntese da função ()
hsl,
sem nenhum cálculo:
* {
--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 deve ser muito escuro. Observe como a claridade das cores a seguir está 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
, como é muito escuro a 10% de brilho, mantém a saturação intensa de 100%
para que a cor da marca ainda possa aparecer na cor azul-marinho escuro.
--text2-light
, ela não é tão escura quanto a primeira cor, que é boa quanto a
secundária, além de ser 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 está posicionado ou dentro. Em um tema claro, são as cores claras, ao contrário das cores de texto que eram escuras. Para criar cores claras com hsl, usaremos valores percentuais maiores no terceiro valor de brilho. Também vamos diminuir a saturação para que os cinzas claros não pareçam muito coloridos.
* {
--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
. Portanto, ao passar o cursor, o resultado aumenta o
contraste (99% de claridade para 92% de claridade, deixando a imagem mais escura).
Sombras
As sombras em um esquema de cores vão além, mas adicionam uma natureza realista ao efeito e o ajudam a se destacar de sombras irrealistas baseadas em preto. Para isso, a cor da sombra usará a propriedade hue custom, que deve ser ligeiramente saturada com a tonalidade, mas ainda muito escura. Basicamente, construir uma sombra claramente azul muito escura.
* {
--surface-shadow-light: var(--brand-hue) 10% 20%;
--shadow-strength-light: .02;
}
--surface-shadow-light
não está unido a uma função hsl. Isso ocorre porque o valor
--shadow-strength
será combinado para criar alguma opacidade, e o CSS precisa
das peças para realizar cálculos. Pule para a seção
rad shadow para saber mais.
Cores claras juntas
Não é necessário caçar 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, ele é uma variante do tema principal, geralmente mais claro. Por outro lado, os usuários costumam escolher um tema escuro para diferentes contextos, como à noite. Esses fatores me levaram a manter duas coisas em mente com os temas escuros:
- Em geral, os usuários ficam no escuro durante o uso desse tema, então teste no escuro.
- As cores precisam diminuir a saturação para não vibrar na tela por serem muito intensas.
Marca
O tema claro usou os valores dos três canais de cores da marca hsl sem alteração, o tema escuro não. A saturação é reduzida pela metade e o brilho reduzido 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 altos valores de luminosidade, o que as aproxima 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 das superfícies precisam ser escuras. As cores abaixo têm baixa luminosidade e saturação, e a primeira superfície é a mais escura (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, pode ser muito difícil enxergar as sombras. Faz sentido porque é difícil escurecer
algo que já está razoavelmente escuro. É aí 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;
}
Veja também quanta saturação há nessa sombra. Consegue notar a cor ao olhar para a interface? Tente remover a saturação do DevTools. Qual você prefere?
Cores escuras 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;
}
Escurecer tema
Esse esquema de cores está relacionado à orquestração de iluminação e saturação. É necessário que haja saturação suficiente para ainda ter um matiz visível. No entanto, também é necessário transmitir apenas as pontuações de contraste, já que o objetivo é que elas sejam escuras e com baixo contraste.
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;
}
Diminuir as cores juntas
* {
--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 o nível de luminosidade mais baixo no conjunto de cores de texto escuro é de 65%, e o menor brilho nas superfícies escuras é de 25%. Isso representa 40% de iluminação entre eles. No tema claro, há 55% de espaço para respiração no tema claro. Manter as diferenças de claridade entre as cores do texto e da superfície em cerca de 40 a 50% pode ajudar a manter as taxas de contraste altas, além de ser uma alavanca sutil de ajuste em caso de pontuações ruins.
Eu chamo isso de "bump bump til ya pass", que é a interação de aumentar o valor de iluminação até que uma ferramenta mostre que estou passando.
Cada um dos temas criados neste desafio passa em pontos de contraste. O esquema de cores escuras tem o menor contraste, mas ainda atende aos requisitos mínimos. Para ajudar outros da equipe a usar boas cores contrastantes, é uma boa ideia criar um nome de 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 radical
Os temas usam uma classe de utilitário chamada .rad-shadow
. Essa sombra foi gerada
nesta ferramenta Smooth Sombra, que eu aprecio muito. Personalizei o snippet gerado com meus próprios cálculos de cores e opacidade. O motivo era criar uma sombra
que eu pudesse ajustar dentro de cada esquema de cores.
Para isso, criei duas variáveis para cada esquema de cores a ser ajustado: uma cor e uma intensidade da sombra. A cor serve para ajustes de saturação e escuridão, enquanto a intensidade é uma maneira fácil de aumentar a intensidade da sombra quando o esquema de cores é escuro. 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, eu faria os ângulos de sombra como um token de design constante, já que a direção da luz precisa ser a mesma entre todas as sombras do design.
Uso dos esquemas de cores
Depois de concluir a predefinição de cores, é hora de transformá-las em propriedades independentes de esquema. O que quero dizer é que, como autor de CSS dentro deste projeto de esquema de cores, raramente é necessário acessar o valor de um esquema de cores específico. Quero facilitar a permanência no tema.
Para isso, o uso do esquema de cores precisa ser feito exclusivamente pelas
propriedades personalizadas genéricas, que vamos definir em breve. Dessa forma,
as pessoas que usam as variáveis de design não precisam se preocupar com qual esquema de cores está
definido no momento, só precisam usar as cores de superfície e do texto. Em vez de
color: var(--text1-light)
, use color: var(--text1)
. Toda adaptação e mudança de cores
são feitas em um nível muito mais alto no CSS.
Analisando os estilos conectivos do tema claro no bloco de código a seguir,
conecte uma propriedade personalizada genérica à cor específica do tema claro. Agora, todos
os usos de var(--brand)
terão a cor da marca clara.
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 está usando o tema claro. É um momento muito divertido! Vamos aproveitar mais alguns momentos, já que usamos as cores predefinidas em outros contextos de esquema 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);
}
Escurecer tema
[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);
}
A esta altura, os autores podem usar o esquema de cores genérico fornecido, conforme necessário, e nunca mais precisam se preocupar com temas.
Conclusão
Agora que você sabe como eu fiz isso, como você iria?! 🙂
Vamos diversificar nossas abordagens e aprender todas as maneiras de criar na Web. Crie um Codepen ou apresente sua própria demonstração, envie um tweet para mim e vou adicioná-la à seção "Remixes da comunidade" abaixo.
Origem
Remixes da comunidade:
@chris-kruining adicionou um controle deslizante de tom,
cores de status e modos de contraste para no-preference
, more
e less
:
demonstração.