Como criar um esquema de cores

Uma 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.

Demo

Se você preferir o 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 crie uma página da Web que se adapte às preferências do usuário, mantendo a criação experiência mínima. Começamos com uma cor de marca de base e criamos um sistema de 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 para cada esquema de cores frente. Só no final é que eles são usados para mudar a página.

A marca

Muitas vezes, a cor de uma marca já foi estabelecida e é fornecida como hex 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 matemáticos nessas propriedades de cor, por exemplo, calc(var(--brand-lightness) - 20%) para diminuir o valor de 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 matiz ajustando a saturação e a luminosidade do HSL.

Tema claro

Cada variante de cor será marcada com o esquema correspondente. é anexado com -light.

visualização dos resultados finais do tema claro

Marca

Começando com a 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 deve ser muito escuro. Observe como o brilho das seguintes cores é baixo, 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 que o texto fica sobre ou dentro dele. Em um tema claro, essas são as cores claras, em oposição às cores de texto, que eram escuras. Para criar cores claras com hsl, vamos usar valores percentuais mais altos no terceiro valor de iluminação. 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 cores decorativas tendem a precisar de mais variantes, para momentos interativos como :focus ou :hover, ou para criar a a aparência das camadas de papel. Nesses cenários, é bom fazer a transição --surface2-light ao passar o cursor para --surface3-light, portanto, passar o cursor resulta em uma aumento do contraste (99% de claridade para 92% de claridade, tornando-o mais escuro).

Sombras

Sombras dentro de um esquema de cores estão acima e além, mas adicionam uma natureza realista ao o efeito e ajudá-lo a se destacar de sombras pretas irrealistas. 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 O valor --shadow-strength será combinado para criar alguma opacidade, e o CSS precisa as peças para realizar cálculos. Vá direto para a sombra radical 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;
}
captura de tela das cores claras todas juntas
Sandbox no CodePen

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 contextos diferentes, como a noite. Esses fatores me fizeram manter duas coisas em mente com os temas escuros:

  1. Os usuários geralmente ficam no escuro ao usar esse tema, então teste no escurecer.
  2. As cores precisam ser desaturadas para não vibrarem na tela por serem muito intensas.

visualização do resultado final do tema escuro

Marca

O tema claro usou os 3 valores dos canais de cor HDR da marca sem alteração, o tema escuro não. A saturação é reduzida pela metade e o brilho, em cerca de 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 alto valores de claridade, colocando-os mais próximos 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 pouca luminosidade e saturação, sendo que a 1a superfície é a mais escura a 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á é 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. Consegue notar a cor quando você está olhando para a interface? Tente remover a saturação do DevTools, 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;
}
captura de tela das cores escuras juntas
Sandbox no CodePen

Tema escuro

Esse esquema de cores é sobre orquestrar a luminosidade e a saturação. deve haver saturação suficiente para ainda ter uma matiz visível, mas também apenas passam as pontuações de contraste, já que é devem ser escuros e de 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;
}

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

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 o 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 bater no lightness até que uma ferramenta mostre que passei.

Pressione Shift + seta para baixo para diminuir a luminosidade e aumentar o contraste até passar

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 os outros membros da equipe a usar 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 pares de texto
Captura de tela da superfície escura 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 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.

cada sombra uma ao lado da outra

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 ficou 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.

Uso dos 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 dentro deste projeto de esquema de cores, raramente é necessário acessar o valor de um esquema de cores específico. Quero facilitar a criação de conteúdo relacionado ao 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 está usando o tema claro. Este é um momento muito divertido de sucesso! Vamos ter mais alguns desses momentos à medida que usamos nossas 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);
}

Nesse ponto, os autores podem usar os genéricos de esquema de cores 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 eu a adicionarei ao 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.