Como configurar esquemas de cores de aplicativos com propriedades personalizadas de CSS

Publicados: 2022-03-10
Resumo rápido ↬ Neste artigo, Artur Basak apresenta uma abordagem moderna sobre como configurar CSS Custom Properties que respondem às cores do aplicativo. A ideia de dividir as cores em três níveis pode ser bastante útil: uma paleta (ou esquema), cores funcionais (ou tema) e cores componentes (escopo local).

As variáveis ​​são uma ferramenta básica que ajuda a organizar as cores em um projeto. Por muito tempo, os engenheiros de front-end usaram variáveis ​​de pré-processador para configurar cores em um projeto. Mas agora, muitos desenvolvedores preferem o mecanismo nativo moderno para organizar as variáveis ​​de cor: CSS Custom Properties. Sua vantagem mais importante sobre as variáveis ​​do pré-processador é que elas funcionam em tempo real, não na fase de compilação do projeto, e têm suporte para o modelo em cascata que permite usar herança e redefinição de valores em tempo real.

Ao tentar organizar um esquema de cores de aplicativo, você sempre pode colocar todas as propriedades personalizadas relacionadas à cor na seção raiz, nomeá-las e usá-las em todos os locais necessários.

Veja a Caneta [Custom Properties for Colors](https://codepen.io/smashingmag/pen/RwaNqxW) de Artur Basak.

Veja as propriedades personalizadas da caneta para cores de Artur Basak.

Essa é uma opção, mas isso ajuda você a resolver problemas de temas de aplicativos, rótulos brancos, atualização de marca ou organização de um modo claro ou escuro? E se você precisar ajustar o esquema de cores para aumentar o contraste? Com a abordagem atual, você terá que atualizar cada valor em suas variáveis.

Neste artigo, quero sugerir uma abordagem mais flexível e resistente sobre como dividir variáveis ​​de cores usando propriedades personalizadas, o que pode resolver muitos desses problemas.

Mais depois do salto! Continue lendo abaixo ↓

Configurar paleta de cores

A coloração de qualquer site começa com a configuração de um esquema de cores. Tal esquema é baseado na roda de cores. Normalmente, apenas algumas cores primárias formam a base de uma paleta, o restante são cores derivadas – tons e tons médios. Na maioria das vezes, a paleta é estática e não muda enquanto o aplicativo da Web está em execução.

De acordo com a teoria das cores, existem apenas algumas opções para esquemas de cores:

  • Esquema monocromático (uma cor primária)
  • Esquema complementar (duas cores primárias)
  • Esquema de tríade (três cores primárias)
  • Esquema tetrádico (quatro cores primárias)
  • Padrão adjacente (duas ou três cores primárias)

Para o meu exemplo, vou gerar um esquema de cores triad usando o serviço Paletton:

Roda de cores com o esquema triádico estabelecido: variação de verde, azul e vermelho.
Serviço Paletton: Esquema Triádico de Cores. (Visualização grande)

Agora tenho três cores principais. Com base nisso, vou calcular os tons e tons médios (o formato HSL em combinação com a função calc é uma ferramenta muito útil para isso). Ao alterar o valor da luminosidade, posso gerar várias cores adicionais para a paleta.

Veja a caneta [HSL Palette](https://codepen.io/smashingmag/pen/OJNPaQW) de Artur Basak.

Veja a paleta Pen HSL de Artur Basak.

Agora, se a paleta for modificada, será necessário alterar apenas o valor das cores primárias. O restante será recalculado automaticamente.

Se você preferir os formatos HEX ou RGB, não importa; uma paleta pode ser formada na fase de compilação do projeto com as funções correspondentes do pré-processador (por exemplo, com SCSS e a função color-adjust ). Como mencionei antes, essa camada é principalmente estática; é extremamente raro que a paleta possa ser alterada em um aplicativo em execução. É por isso que podemos calculá-lo com pré-processadores.

Nota : Eu recomendo também gerar literal HEX e RGB para cada cor. Isso permitirá jogar com o canal alfa no futuro.

Veja a Caneta [Paleta SCSS](https://codepen.io/smashingmag/pen/oNxgQqv) de Artur Basak.

Veja a Paleta Pen SCSS de Artur Basak.

O nível da paleta é o único nível onde a cor é codificada diretamente nos nomes das variáveis, ou seja, podemos identificar de forma única a cor lendo o nome.

Definir Tema ou Cores Funcionais

Feita a paleta, o próximo passo é o nível de cores funcionais . Nesse nível, o valor da cor não é tão importante quanto sua finalidade, a função que desempenha e o que exatamente colore. Por exemplo, a cor principal ou da marca do aplicativo, a cor da borda, a cor do texto em um plano de fundo escuro, a cor do texto em um plano de fundo claro, a cor do plano de fundo do botão, a cor do link, a cor do link ao passar o mouse, a cor do texto de dica e assim por diante .

Essas são coisas extremamente comuns para quase qualquer site ou aplicativo. Podemos dizer que tais cores são responsáveis ​​por um determinado tema de cores do aplicativo. Além disso, os valores de tais variáveis ​​são retirados estritamente da paleta. Assim, podemos alterar facilmente os temas do aplicativo simplesmente operando com diferentes paletas de cores.

Abaixo, criei três controles de interface do usuário típicos: um botão, um link e um campo de entrada. Eles são coloridos usando variáveis ​​funcionais que contêm valores da paleta que gerei anteriormente. A principal variável funcional responsável pelo tema do aplicativo (marca condicional) é a variável de cor primária.

Usando os três botões na parte superior, você pode alternar os temas (alterar a cor da marca para os controles). A alteração ocorre usando a API CSSOM apropriada (setProperty).

Veja a Caneta [Cores Funcionais](https://codepen.io/smashingmag/pen/poyvQLL) de Artur Basak.

Veja as Cores Funcionais da Caneta de Artur Basak.

Essa abordagem é conveniente não apenas para temas, mas também para configurar páginas da Web individuais. Por exemplo, no site zubry.by, usei uma folha de estilo comum e uma variável funcional --page-color para colorir o logotipo, títulos, controles e seleção de texto para todas as páginas. E nos estilos próprios de cada página, apenas redefini essa variável para definir a página com sua cor primária individual.

3 páginas web do site ZUBRY.BY: página de selos, página de postais e página de cartões.
ZUBRY.BY site onde cada página tem cor primária individual. (Visualização grande)

Usar cores de componentes

Grandes projetos da web sempre contêm decomposição; dividimos tudo em pequenos componentes e os reutilizamos em muitos lugares. Cada componente geralmente tem seu próprio estilo, o que significa que não importa o que usamos para decompor os módulos BEM ou CSS, ou outra abordagem; é importante que cada pedaço de código possa ser chamado de escopo local e reutilizado.

Em geral, vejo sentido em usar variáveis ​​de cor no nível do componente em dois casos.

A primeira é quando componentes que de acordo com o guia de estilo de aplicação são repetidos com configurações diferentes, por exemplo, botões para diferentes necessidades como botão primário (marca), botão secundário, terciário e assim por diante.

Diferentes estilos de botão para aplicação Tispr.
Guia de estilo do aplicativo Tispr. Botões. (Visualização grande)

A segunda é quando componentes que possuem vários estados com cores diferentes, por exemplo, botão hover, estado ativo e estado de foco; estados normais e inválidos para campo de entrada ou seleção, e assim por diante.

Um caso mais raro em que variáveis ​​de componentes podem ser úteis é a funcionalidade de um “white label”. O “white label” é um recurso de serviço que permite ao usuário personalizar ou marcar alguma parte da interface do usuário para melhorar a experiência de interação com seus clientes. Por exemplo, documentos eletrônicos que um usuário compartilha com seus clientes por meio de um serviço ou modelos de e-mail. Nesse caso, as variáveis ​​no nível do componente ajudarão a configurar determinados componentes separadamente do restante do tema de cores do aplicativo.

No exemplo abaixo, agora adicionei controles para personalizar as cores do botão primário (marca). Usando variáveis ​​de cor do nível do componente, podemos configurar os controles da interface do usuário separadamente uns dos outros.

Veja a Caneta [Component Colors](https://codepen.io/smashingmag/pen/LYNEXdw) de Artur Basak.

Veja as Cores dos Componentes da Caneta de Artur Basak.

Como determinar o nível de uma variável?

Me deparei com a questão de como entender o que pode ser colocado na raiz (tema ou nível funcional), e o que deixar no nível de um componente. Esta é uma excelente pergunta que é difícil de responder sem ver a situação com a qual você está trabalhando.

Infelizmente, a mesma abordagem da programação não funciona com cores e estilos, se vemos três partes idênticas de código, precisamos refatorá-lo.

A cor pode ser repetida de componente para componente, mas isso não significa que seja uma regra. Não pode haver relação entre tais componentes. Por exemplo, a borda do campo de entrada e o plano de fundo do botão principal. Sim, no meu exemplo acima esse é o caso, mas vamos verificar o seguinte exemplo:

Veja a Caneta [Color Split: Only Palette](https://codepen.io/smashingmag/pen/YzqPRLX) de Artur Basak.

Veja a Pen Color Split: Only Palette de Artur Basak.

A cor cinza escuro é repetida — essa é a borda do campo de entrada, a cor de preenchimento do ícone de fechamento e o plano de fundo do botão secundário. Mas esses componentes não estão de forma alguma conectados uns com os outros. Se a cor da borda do campo de entrada for alterada, não alteraremos o plano de fundo do botão secundário. Para tal caso devemos manter aqui apenas a variável da paleta.

Controles da interface do usuário: botões, link, cabeçalho e textos regulares, campo de entrada
Exemplo de guia de estilo de aplicativo. (Visualização grande)

E o verde? Podemos defini-lo claramente como a cor primária ou da marca, provavelmente, se a cor do botão principal mudar, a cor do link e do cabeçalho do primeiro nível também mudará.

E o vermelho? O estado inválido dos campos de entrada, mensagens de erro e os botões destrutivos terão a mesma cor em todo o nível do aplicativo. Este é um padrão. Agora posso definir várias variáveis ​​funcionais comuns na seção raiz:

Veja a Caneta [Color Split: Functional Level](https://codepen.io/smashingmag/pen/MWyYzGX) de Artur Basak.

Veja o Pen Color Split: Functional Level de Artur Basak.

Em relação ao nível de cores dos componentes, podemos identificar facilmente os componentes que podem ser personalizados usando propriedades personalizadas.

O botão é repetido com configurações diferentes, a cor de fundo e o texto para diferentes casos de uso mudam - caso primário, secundário, terciário, destrutivo ou negativo.

O campo de entrada tem dois estados — incorreto e normal, onde as cores de fundo e de borda diferem. E assim, vamos colocar essas configurações em variáveis ​​de cor no nível dos componentes correspondentes.

Para os demais componentes, não é necessário definir variáveis ​​de cores locais, isso será redundante.

Veja a Caneta [Color Split: Component Level](https://codepen.io/smashingmag/pen/BaKyGVR) de Artur Basak.

Veja o Pen Color Split: Component Level de Artur Basak.

Você precisa mergulhar na linguagem de padrões do seu projeto, que provavelmente está sendo desenvolvido pela equipe de design e UX. Os engenheiros devem entender completamente todo o conceito de uma linguagem visual, só assim podemos determinar o que é comum e deve viver em um nível funcional, e o que deve permanecer no escopo local de visibilidade.

Mas nem tudo é tão complicado, há coisas óbvias. O plano de fundo geral da página, o plano de fundo e a cor do texto principal, na maioria dos casos, é isso que define o tema do seu aplicativo. É extremamente conveniente coletar essas coisas que são responsáveis ​​pela configuração de um modo específico (como o modo escuro ou claro).

Por que não colocar tudo na seção raiz?

Eu tive uma experiência assim. No projeto Lition, a equipe e eu nos deparamos com o fato de que precisávamos oferecer suporte ao IE11 para o aplicativo da Web, mas não para o site e os desembarques. Um UI Kit comum foi usado entre os projetos, e decidimos colocar todas as variáveis ​​na raiz, isso nos permitirá redefini-las em qualquer nível.

E também com essa abordagem para o aplicativo da Web e o caso do IE11, simplesmente passamos o código pelo plug-in de pós-processador a seguir e transformamos essas variáveis ​​em literais para todos os componentes de interface do usuário no projeto. Este truque só é possível se todas as variáveis ​​forem definidas na seção raiz porque o pós-processador não consegue entender as especificidades do modelo em cascata.

Página principal do site Lition com ferramentas de desenvolvimento de navegador abertas
Lição SSR web-site. Todas as variáveis ​​na seção raiz. (Visualização grande)

Agora eu entendo que este não era o caminho certo. Em primeiro lugar, se você colocar cores de componentes na seção raiz, quebrará o princípio da separação de interesses. Como resultado, você pode acabar com CSS redundante na folha de estilo. Por exemplo, você tem a pasta de componentes onde cada componente tem seus próprios estilos. Você também tem uma folha de estilo comum onde descreve as variáveis ​​de cor na seção raiz. Você decide remover o componente de botão; neste caso, lembre-se de remover também as variáveis ​​associadas ao botão do arquivo de estilos comuns.

Em segundo lugar, esta não é a melhor solução em termos de desempenho. Sim, uma mudança de cor causa apenas o processo de repintura, não de reflow/layout, isso por si só não é muito caro, mas quando você faz algumas alterações no nível mais alto, você usará mais recursos para verificar a árvore inteira do que quando estas as mudanças estão em uma pequena área local. Eu recomendo ler o benchmark de desempenho de variáveis ​​CSS de Lisi Linhart para mais detalhes.

No meu projeto atual Tispr, a equipe e eu usamos split e não despejo tudo na raiz, no alto nível apenas uma paleta e cores funcionais. Além disso, não temos medo do IE11, porque esse problema é resolvido pelo polyfill correspondente. Basta instalar o módulo npm ie11-custom-properties e importar a biblioteca para o pacote JS do seu aplicativo:

 // Use ES6 syntax import "ie11-custom-properties"; // or CommonJS require('ie11-custom-properties');

Ou adicione módulo por tag de script:

 <script async src="./node_modules/ie11-custom-properties/ie11CustomProperties.js">

Além disso, você pode adicionar a biblioteca sem npm via CDN. O trabalho deste polyfill é baseado no fato de que o IE11 tem suporte mínimo para propriedades customizadas, onde as propriedades podem ser definidas e lidas com base na cascata. Isso não é possível com propriedades que começam com traços duplos, mas possivelmente com um único traço (o mecanismo semelhante aos prefixos do fornecedor). Você pode ler mais sobre isso na documentação do repositório, bem como se familiarizar com alguns limites. Outros navegadores irão ignorar este polyfill.

Abaixo está uma paleta do aplicativo da web Tispr, bem como os controles da funcionalidade “white label” para os documentos eletrônicos (como contratos de usuário, faturas ou propostas).

Grade com as seguintes colunas: cor, nome da cor, cor HEX, cor RGB.
Guia de Estilo Tispr: Paleta de Cores. (Visualização grande)
Componente de interface do usuário do seletor de cores personalizado
Tispr Styleguide: Selecionador de Marcas para a funcionalidade White Label. (Visualização grande)

Por que não armazenar variáveis ​​de cor no lado do JavaScript?

Outra pergunta razoável: por que não armazenar as variáveis ​​de paleta e função no código JavaScript? Isso também pode ser alterado dinamicamente e as cores redefinidas posteriormente por meio de estilos embutidos. Isso pode ser uma opção, mas provavelmente essa abordagem seria menos ideal, pois você precisa ter acesso a determinados elementos e alterar suas propriedades de cor. Com variáveis ​​CSS, você mudará apenas uma única propriedade, ou seja, o valor da variável.

Em JavaScript, não há funções nativas ou API para trabalhar com cores. No CSS Color Module 5, haverá muitas oportunidades para criar cores derivadas ou de alguma forma calculá-las. Da perspectiva do futuro, as propriedades personalizadas do CSS são mais ricas e flexíveis do que as variáveis ​​do JS. Além disso, com variáveis ​​JS, não haverá possibilidade de usar herança em cascata e essa é a principal desvantagem.

Conclusão

A divisão de cores em três níveis (paleta, funcional e componente) pode ajudá-lo a se adaptar melhor a mudanças e novos requisitos enquanto trabalha em um projeto. Acredito que CSS Custom Properties é a ferramenta certa para organizar a divisão de cores — não importa o que você usa para estilizar: CSS puro, pré-processadores ou abordagem CSS-in-JS.

Cheguei a essa abordagem por experiência própria, mas não estou sozinho. Sara Soueidan descreveu em seu artigo uma abordagem semelhante na qual ela dividia variáveis ​​em níveis globais e de componentes.

Também gostaria de sugerir a leitura do artigo da Lea Verou onde ela descreve possíveis casos de aplicação de variáveis ​​CSS (não apenas em termos de cor).