Custom Properties, Design Systems, Escalabilidade

banner relacionado ao assunto do tutorial

Introdução

Em projetos de grande escala, a consistência e a manutenção são desafios constantes. Por muitos anos, dependemos de pré-processadores como SASS e LESS para introduzir o conceito de variáveis em CSS. No entanto, o CSS moderno, através das Custom Properties, redefiniu o que uma "variável" pode ser, transformando-a em um Token de Design, uma entidade que reside no runtime do navegador e participa ativamente da cascata.

O Problema do CSS Clássico (pré-processadores) é que ele gera CSS estático no build time, o que limita sua flexibilidade e adaptabilidade em tempo de execução. Variáveis de pré-processadores, por exemplo $cor-primaria: #007bff; no SASS, são resolvidas em tempo de build. O navegador recebe apenas o valor compilado. O pré-processador substitui $cor-primaria antes de entregar ao navegador o CSS final:
CSS:
.botao-primario {
        background-color: #007bff; /* Valor fixo */
        }

Isso significa que você não pode alterar essa cor dinamicamente com JavaScript e nem pode definir a cor em um escopo específico, tal como um componente aninhado, de forma que ela se propague para baixo na cascata apenas naquele contexto.

Em CSS Sênior, Custom Properties atuam como tokens vivos, pois são valores de propriedade que participam da cascata e herança, são dinâmicos, acessíveis e contextuais podendo ser manipulados em tempo de execução pelo JavaScript.

A sintaxe básica é conforme mostgrada a seguir:
CSS:
/* Definição de uma Custom Property */
:root {
--cor-principal: #007bff;
}

/* Uso da Custom Property */
.botao {
background-color: var(--cor-principal);
}

A partir daqui, trataremos Custom Properties como a espinha dorsal de qualquer Design System escalável.

A Cascata é sua aliada, não sua inimiga

O poder das Custom Properties reside na sua interação com a cascata. Um erro comum é pensar nelas como variáveis de programação tradicionais. Em vez disso, pense nelas como propriedades CSS herdáveis e sobregraváveis.

Entendendo o EscopoA chave para a arquitetura sênior é a definição estratégica de onde cada variável deve ser declarada.Local de DefiniçãoEscopo e Finalidade:rootGlobal / Padrão (Defaults): Define os valores que se aplicam a todo o documento, servindo como a fonte de verdade primária..tema-darkContextual / Tema: Sobrescreve valores globais para um contexto específico (troca de tema)..componente-cardLocal / Módulo: Variáveis que só fazem sentido dentro de um módulo, isolando-o do escopo global.

Exemplo de Escopo Estratégico:

CSS:
:root {
/* Default Global: Tema claro */
--cor-fundo-primaria: white;
--cor-texto-primaria: black;
}

/* Sobrescrita Contextual: Tema escuro */
.tema-dark {
--cor-fundo-primaria: #1a1a1a;
--cor-texto-primaria: #f0f0f0;
}

/* Aplicação: Componente herda o valor que estiver ativo no escopo */
.cabecalho {
background-color: var(--cor-fundo-primaria);
color: var(--cor-texto-primaria);
}

Nesse exemplo a classe .cabecalho recebe a cor de fundo white e a cor do texto black por padrão, mas se pertencer a um elemento pai com a classe .tema-dark, ela herda os novos valores, cor de fundo #1a1a1ae cor do texto#f0f0f0.

O Valor de fallback var(--var, fallback) deve ser definido estrategicamente para aumentar a robustez do sistema sem mascarar problemas de arquitetura. Por exemplo, em cenários onde a variável pode não estar definida (p.ex: componente reutilizado em um contexto inesperado).

Se as cores de fundo e texto do tema escuro não estiverem definidas

CSS:
.cabecalho {
background-color: var(--cor-fundo-primaria, #e0e0e0;); /* fallback cinza claro */
color: var(--cor-texto-primaria, #333;); /* fallback cinza escuro */
}

Ao incluir o fallback var(--variavel, valor-seguro), elevamos a robustez do componente .cabecalho:

Este é o uso correto e sênior do fallback no contexto de temas.

Gerenciamento de temas

Esta é a aplicação mais canônica e eficiente das Custom Properties para escalabilidade. A troca de temas torna-se trivial e performática.

A chave para um sistema de temas robusto é separar o USO da APARÊNCIA da cor. Fazemos isso definindo tokens de design abstratos que são sobregravados por classes de escopo (p.ex: .tema-dark). A ideia é definir a paleta de cores de forma abstrata e mapear essas abstrações para os valores reais de cor em diferentes escopos.

Definição de tokens abstratos:
No seletor :root, definimos variáveis baseadas em uso, não em valor (ex: --cor-fundo-pagina em vez de --cor-azul-claro).

Mapeamento de Tema: Criamos uma classe (ex: .tema-dark) para o escopo que precisa da mudança. Dentro dessa classe, sobrescrevemos os tokens abstratos.
CSS:
/* 1. Tokens Abstratos e Default (Tema Claro) */
:root {
--cor-fundo-pagina: white;
--cor-texto-principal: #333;
--cor-realce: #007bff;
}

/* 2. Mapeamento para Tema Escuro */
.tema-dark {
--cor-fundo-pagina: #1f1f1f;
--cor-texto-principal: #f0f0f0;
/* A cor de realce pode permanecer a mesma em ambos os temas */
}

/* Aplicação dos Tokens */
body {
background: var(--cor-fundo-pagina);
color: var(--cor-texto-principal);
}
a {
color: var(--cor-realce);
}

A mudança de tema agora requer apenas que o JavaScript adicione ou remova a classe .tema-dark no elemento

A mudança de tema, portanto, é resolvida com o JavaScript realizando uma única ação, adicionar ou remover a classe .tema-dark no elemento body ou :root. Como as Custom Properties participam da Cascata e da herança, o navegador recalcula os estilos instantaneamente, garantindo uma transição de tema altamente performática sem a necessidade de reprocessamento complexo.

Arquitetando espaçamento e tipografia fluída

Um Design System robusto não usa valores mágicos (p.ex: 17px, 23px). Ele usa uma escala modular para espaçamento e tipografia, baseada em um múltiplo ou proporção. Custom Properties, combinadas com a função calc(), tornam isso gerenciável.

Para criar uma Escala Baseada em Unidade definimos uma unidade base e uma proporção de escala, calculando todas as variações necessárias:

CSS:
:root {
/* Unidade Base (Grid) */
--unidade-base: 1rem;
/* Proporção para a escala (Ex: Major Third - 1.25) */
--escala-proporcao: 1.25;
/* Níveis de Escala Calculados */
--espacamento-sm: calc(var(--unidade-base) / var(--escala-proporcao)); /* 0.8rem */
--espacamento-md: var(--unidade-base); /* 1rem (o padrão) */
--espacamento-lg: calc(var(--unidade-base) * var(--escala-proporcao)); /* 1.25rem */
--espacamento-xl: calc(var(--espacamento-lg) * var(--escala-proporcao)); /* 1.5625rem */
}

A Magia da Manutenção e Adaptação

Se decidirmos que a escala 1.25 é muito grande, você só precisa alterar um valor, --escala-proporcao, e todos os 4 níveis de espaçamento no seu projeto se ajustam automaticamente.

Para criar tipografia fluida ou espaçamento que escala com o tamanho da tela, uma prática sênior de design responsivo, basta redefinir a variável base dentro de uma media query:

CSS:
/* Dispositivos Móveis (Unidade Base menor) */
:root {
--unidade-base: 0.875rem; /* 14px */
}
/* Telas Grandes (Unidade Base maior) */
@media (min-width: 768px) {
:root {
--unidade-base: 1.125rem; /* 18px */
}
}
/* Todos os 'espacamento-sm, md, lg, xl' se ajustam automaticamente! */

O uso de Custom Properties para definir escalas e tokens transforma a manutenção de um Design System de uma tarefa de busca e substituição para uma simples mudança de configuração.

Custom Properties como estado e interação

Custom Properties são a ponte mais eficiente entre o CSS e o JavaScript para controle de UI, permitindo que você armazene estado no CSS e manipule-o com o JS.

O JavaScript pode facilmente ler e escrever Custom Properties em qualquer elemento, usando setProperty e getPropertyValue:

JS:
const elemento = document.querySelector('.menu');
// 1. Escrever (Alterar o estado)
elemento.style.setProperty('--estado-aberto', '1');

// 2. Ler (Verificar o estado)
const estado = elemento.style.getPropertyValue('--estado-aberto').trim();
console.log(estado); // Saída: 1

Em vez de usar JavaScript para adicionar e remover classes para cada estágio de uma transição, use uma Custom Property para armazenar o valor do deslocamento (offset):

html

JS:
.dropdown {
/* Estado inicial: fechado (offset de 10px para cima) */
--deslocamento-y: 10px;
--opacidade: 0;

transition: transform 0.3s, opacity 0.3s;
transform: translateY(var(--deslocamento-y));
opacity: var(--opacidade);
}

.dropdown.aberto {
/* A transição ocorre quando o JS altera as variáveis */
--deslocamento-y: 0;
--opacidade: 1;
}

Com esta abordagem, você mantém toda a lógica de animação no CSS, mas permite que o JavaScript defina o destino da animação alterando as Custom Properties. Isso é extremamente poderoso para performance, pois você está apenas atualizando uma propriedade, e não forçando o navegador a recalcular o layout devido a múltiplas mudanças de classe.

Compartilhe essa matéria com seus amigos

logo twitter logo facebook logo linkedin

Conheça os livros do Maujor®

Visite o site dos livros do Maujor.

Os sites do Maujor estão hospedados na DialHost.