Como criar um esquema de cores

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.

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.

visualização dos resultados finais do tema claro

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;
}
captura de tela das cores das luzes juntas
Sandbox no CodePen

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:

  1. Em geral, os usuários ficam no escuro durante o uso desse tema, então teste no escuro.
  2. As cores precisam diminuir a saturação para não vibrar na tela por serem muito intensas.

visualização do resultado final do tema escuro

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;
}
captura de tela das cores escuras juntas
Sandbox no CodePen

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.

visualização dos resultados finais do tema escuro

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;
}
captura de tela das cores escuras juntas
Sandbox no CodePen

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.

Shift + seta para baixo é pressionada para diminuir o brilho e aumentar o contraste até passar

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);
}
Captura de tela da superfície escura e dos pareamentos de texto
Captura de tela da superfície escurecida e dos pareamentos de texto com o VisBug

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.

cada sombra uma ao lado da outra

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.