Capa do Ebook gratuito Arquitetura de CSS Escalável: BEM, ITCSS e Design Tokens para Projetos Reais

Arquitetura de CSS Escalável: BEM, ITCSS e Design Tokens para Projetos Reais

Novo curso

22 páginas

Implementação de design tokens em CSS: custom properties, escalas e fallback

Capítulo 10

Tempo estimado de leitura: 11 minutos

+ Exercício

O que significa “implementar” design tokens em CSS

Depois de definir tokens (cores, tipografia, espaçamentos, raios, sombras etc.), o próximo passo é torná-los utilizáveis no código com consistência, previsibilidade e capacidade de evolução. Implementar tokens em CSS significa: (1) escolher um formato de armazenamento e distribuição, (2) expor os valores como variáveis consumíveis pelos componentes, (3) criar escalas coerentes (em vez de valores soltos), e (4) garantir fallbacks para compatibilidade, resiliência e degradação controlada.

Em CSS moderno, a forma mais prática de expor tokens é via custom properties (variáveis CSS). Elas permitem herança, troca por tema, ajuste por contexto e atualização em runtime (por exemplo, alternar tema claro/escuro sem recompilar). Porém, para que isso funcione bem em projetos reais, é essencial separar tokens “base” (primitivos) de tokens “semânticos” (de intenção), definir escalas e aplicar fallbacks.

Custom properties: como e onde declarar tokens

Custom properties são declaradas com nomes iniciando por -- e acessadas com var(--nome). A decisão mais importante é o escopo: declarar em :root torna global; declarar em um seletor de tema (ex.: [data-theme="dark"]) permite sobrescrever; declarar em um componente permite variações locais.

Declaração mínima em :root

:root {  --color-blue-500: #2563eb;  --color-slate-900: #0f172a;  --space-4: 1rem;  --radius-2: 0.5rem;  --shadow-2: 0 8px 24px rgba(15, 23, 42, 0.12);}

Esse formato já funciona, mas tende a virar um “catálogo” difícil de consumir diretamente. Por isso, em projetos escaláveis, costuma-se criar uma camada de tokens semânticos que apontam para os primitivos.

Tokens primitivos vs. tokens semânticos (implementação em CSS)

Primitivos são valores brutos organizados em escalas (ex.: --color-blue-500, --space-4). Semânticos representam intenção de uso (ex.: --color-bg, --color-text, --space-stack). Componentes devem preferir semânticos, porque eles permitem mudar a aparência do sistema sem refatorar cada componente.

Continue em nosso aplicativo

Você poderá ouvir o audiobook com a tela desligada, ganhar gratuitamente o certificado deste curso e ainda ter acesso a outros 5.000 cursos online gratuitos.

ou continue lendo abaixo...
Download App

Baixar o aplicativo

:root {  /* Primitivos */  --color-slate-0: #ffffff;  --color-slate-900: #0f172a;  --color-blue-500: #2563eb;  --color-red-600: #dc2626;  --space-1: 0.25rem;  --space-2: 0.5rem;  --space-3: 0.75rem;  --space-4: 1rem;  --space-6: 1.5rem;  --radius-1: 0.25rem;  --radius-2: 0.5rem;  --radius-pill: 9999px;  /* Semânticos */  --color-bg: var(--color-slate-0);  --color-text: var(--color-slate-900);  --color-primary: var(--color-blue-500);  --color-danger: var(--color-red-600);  --space-inline: var(--space-3);  --space-stack: var(--space-4);  --radius-control: var(--radius-2);}

Note que tokens semânticos são definidos com var() apontando para primitivos. Isso cria um “contrato” de consumo: componentes usam --color-primary e não precisam saber qual azul exato é usado.

Escalas: como evitar valores arbitrários e manter ritmo visual

Escalas são sequências de valores com passos previsíveis. Em CSS, escalas bem implementadas reduzem retrabalho e evitam “um valor novo” a cada ajuste. O objetivo é que a maioria das decisões caia em um conjunto pequeno de opções, e que essas opções se relacionem entre si.

Escala de espaçamento (ex.: 4px base com rem)

Uma prática comum é mapear uma escala baseada em múltiplos consistentes. Em vez de usar pixels diretamente, prefira rem para respeitar preferências de fonte do usuário. Se 1rem = 16px, então 0.25rem = 4px, 0.5rem = 8px, etc.

:root {  --space-0: 0;  --space-1: 0.25rem; /* 4px */  --space-2: 0.5rem;  /* 8px */  --space-3: 0.75rem; /* 12px */  --space-4: 1rem;    /* 16px */  --space-5: 1.25rem; /* 20px */  --space-6: 1.5rem;  /* 24px */  --space-8: 2rem;    /* 32px */  --space-10: 2.5rem; /* 40px */  --space-12: 3rem;   /* 48px */}

Mesmo que você não use todos os degraus, a escala existe para evitar “buracos” e escolhas aleatórias. Se surgir uma necessidade recorrente fora da escala, você ajusta a escala (com critério) em vez de adicionar valores pontuais.

Escala tipográfica com custom properties

Você pode definir tamanhos de fonte com nomes que indiquem degraus, e também tokens semânticos para casos comuns (texto, título, legenda). Uma forma simples é usar uma escala com multiplicador (ex.: 1.125) e mapear manualmente os degraus mais usados.

:root {  --font-family-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Arial, "Noto Sans", "Liberation Sans", sans-serif;  --font-weight-regular: 400;  --font-weight-medium: 500;  --font-weight-bold: 700;  --font-size-1: 0.875rem; /* 14px */  --font-size-2: 1rem;     /* 16px */  --font-size-3: 1.125rem; /* 18px */  --font-size-4: 1.25rem;  /* 20px */  --font-size-5: 1.5rem;   /* 24px */  --line-height-tight: 1.2;  --line-height-base: 1.5;  --line-height-loose: 1.7;  /* Semânticos */  --text-body-size: var(--font-size-2);  --text-body-line: var(--line-height-base);  --text-title-size: var(--font-size-5);  --text-title-line: var(--line-height-tight);}

Componentes podem consumir --text-body-size e --text-title-size sem depender do degrau numérico. Se a escala mudar, você ajusta os semânticos.

Escala de cores: primitivos por intensidade e semânticos por intenção

Para cores, é comum ter primitivos por “família” e intensidade (ex.: 50–900). Em CSS, você não precisa expor toda a paleta se não for útil, mas é importante que as intensidades sejam consistentes.

:root {  --color-blue-50: #eff6ff;  --color-blue-100: #dbeafe;  --color-blue-500: #2563eb;  --color-blue-700: #1d4ed8;  --color-slate-0: #ffffff;  --color-slate-100: #f1f5f9;  --color-slate-900: #0f172a;  /* Semânticos */  --color-surface: var(--color-slate-0);  --color-surface-muted: var(--color-slate-100);  --color-text: var(--color-slate-900);  --color-link: var(--color-blue-700);  --color-primary: var(--color-blue-500);}

O ganho prático: um botão “primário” usa --color-primary, não “blue-500”. Se o primário mudar para outra família (verde, roxo), os componentes não mudam.

Fallbacks: garantindo robustez e compatibilidade

Fallback é o valor alternativo usado quando uma custom property não está definida (ou quando você quer garantir um valor mínimo). Em CSS, o fallback é passado como segundo argumento de var(): var(--token, valor).

Fallback para token ausente

.c-alert {  background: var(--color-danger-bg, #fee2e2);  color: var(--color-danger-text, #7f1d1d);  border-radius: var(--radius-control, 0.5rem);  padding: var(--space-4, 1rem);}

Esse padrão é útil quando: (1) você está migrando um projeto legado para tokens, (2) existe risco de um tema não definir todos os semânticos, (3) você distribui componentes que podem ser usados fora do seu contexto principal.

Fallback em cadeia (com cuidado)

Você pode encadear fallbacks usando var() dentro de var(). Isso permite priorizar um token semântico e cair para um primitivo, e por fim para um valor literal.

.c-button {  background: var(--button-bg, var(--color-primary, #2563eb));  color: var(--button-text, var(--color-on-primary, #ffffff));}

Use esse recurso com moderação para não criar uma rede difícil de depurar. Uma regra prática: mantenha a cadeia curta (2 ou 3 níveis no máximo) e documente a intenção dos tokens.

Fallback para navegadores sem suporte a custom properties

Hoje, o suporte a custom properties é amplo, mas ainda pode existir necessidade de degradação em ambientes restritos. O fallback clássico é declarar primeiro um valor “fixo” e depois o valor com var(). Navegadores sem suporte ignoram a segunda declaração.

.c-card {  background: #ffffff;  background: var(--color-surface);  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);  box-shadow: var(--shadow-2);}

Esse padrão também é útil durante migrações: você mantém o visual estável enquanto introduz tokens gradualmente.

Passo a passo prático: implementando tokens com temas e consumo por componentes

A seguir, um fluxo prático para sair de um conjunto de tokens e chegar a um uso consistente em componentes, com suporte a tema e fallbacks.

Passo 1: criar o arquivo de tokens e separar primitivos/semânticos

Organize tokens em um arquivo dedicado (por exemplo, tokens.css). A ideia é centralizar a fonte de verdade.

/* tokens.css */:root {  /* Primitivos: cores */  --color-slate-0: #ffffff;  --color-slate-100: #f1f5f9;  --color-slate-900: #0f172a;  --color-blue-500: #2563eb;  --color-blue-700: #1d4ed8;  /* Primitivos: espaçamento */  --space-2: 0.5rem;  --space-3: 0.75rem;  --space-4: 1rem;  --space-6: 1.5rem;  /* Primitivos: raio e sombra */  --radius-2: 0.5rem;  --shadow-1: 0 1px 2px rgba(15, 23, 42, 0.08);  --shadow-2: 0 8px 24px rgba(15, 23, 42, 0.12);  /* Semânticos: superfícies e texto */  --color-bg: var(--color-slate-100);  --color-surface: var(--color-slate-0);  --color-text: var(--color-slate-900);  --color-link: var(--color-blue-700);  --color-primary: var(--color-blue-500);  /* Semânticos: dimensões comuns */  --radius-surface: var(--radius-2);  --space-surface-padding: var(--space-4);}

Repare que os semânticos já refletem “onde” a cor é usada (fundo, superfície, texto, link). Isso reduz a chance de um componente “pegar emprestado” um primitivo inadequado.

Passo 2: criar temas sobrescrevendo apenas semânticos

Para tema, prefira sobrescrever semânticos. Assim, o tema não precisa conhecer toda a paleta primitiva (a não ser que você queira).

/* theme-dark.css */[data-theme="dark"] {  --color-bg: #0b1220;  --color-surface: #0f172a;  --color-text: #e2e8f0;  --color-link: #93c5fd;  --color-primary: #60a5fa;  --shadow-1: 0 1px 2px rgba(0, 0, 0, 0.35);  --shadow-2: 0 12px 28px rgba(0, 0, 0, 0.45);}

Você pode aplicar o tema no html ou em um container específico:

<html data-theme="dark">...</html>

Ou, para uma área específica:

<div class="app" data-theme="dark">...</div>

Como custom properties herdam, tudo dentro do escopo recebe os novos valores.

Passo 3: consumir tokens em estilos globais de base

Defina o básico do documento usando semânticos. Isso garante que o tema funcione de forma abrangente.

body {  background: var(--color-bg, #f1f5f9);  color: var(--color-text, #0f172a);  font-family: var(--font-family-sans, system-ui);}

Mesmo que você não tenha definido --font-family-sans no exemplo anterior, o fallback mantém o comportamento previsível.

Passo 4: consumir tokens em componentes sem “vazar” primitivos

Um componente deve usar tokens semânticos (ou tokens específicos do componente) e ter fallbacks razoáveis. Exemplo de card:

.c-card {  background: var(--color-surface, #ffffff);  color: var(--color-text, #0f172a);  border-radius: var(--radius-surface, 0.5rem);  box-shadow: var(--shadow-1, 0 1px 2px rgba(15, 23, 42, 0.08));  padding: var(--space-surface-padding, 1rem);}

Exemplo de link:

.c-link {  color: var(--color-link, #1d4ed8);  text-decoration: underline;  text-underline-offset: 0.15em;}

Exemplo de botão primário com tokens do componente (útil para variações locais):

.c-button {  --button-bg: var(--color-primary, #2563eb);  --button-text: var(--color-on-primary, #ffffff);  --button-radius: var(--radius-control, 0.5rem);  --button-py: var(--space-2, 0.5rem);  --button-px: var(--space-4, 1rem);  background: var(--button-bg);  color: var(--button-text);  border-radius: var(--button-radius);  padding: var(--button-py) var(--button-px);  border: 0;}

Esse padrão cria uma “API” interna do componente. Se você precisar de uma variação, pode sobrescrever --button-bg no contexto sem mexer nas regras principais.

Passo 5: criar variações por contexto usando sobrescrita de tokens

Em vez de duplicar regras, altere tokens no escopo da variação. Exemplo: botão em superfície escura dentro de um banner.

.c-banner {  background: var(--color-primary, #2563eb);  color: var(--color-on-primary, #ffffff);  padding: var(--space-6, 1.5rem);}.c-banner .c-button {  --button-bg: rgba(255, 255, 255, 0.16);  --button-text: #ffffff;}

O componente continua o mesmo; apenas seus tokens internos mudam no contexto do banner.

Custom properties e escalas responsivas com clamp()

Tokens também podem representar valores fluidos. Para tipografia e espaçamento responsivos, clamp() permite definir um mínimo, um valor fluido e um máximo. Isso reduz a necessidade de múltiplos breakpoints para ajustes finos.

:root {  --font-size-fluid-1: clamp(0.95rem, 0.9rem + 0.3vw, 1.05rem);  --font-size-fluid-2: clamp(1.1rem, 1rem + 0.6vw, 1.35rem);  --space-fluid-4: clamp(0.9rem, 0.8rem + 0.6vw, 1.25rem);  /* Semânticos */  --text-body-size: var(--font-size-fluid-1);  --space-surface-padding: var(--space-fluid-4);}

O componente não precisa saber se o valor é fixo ou fluido; ele apenas consome o token semântico.

Fallbacks e acessibilidade: tokens para contraste e estados

Tokens ajudam a padronizar estados (hover, focus, disabled) e a manter contraste. Uma abordagem prática é definir tokens semânticos para estados e garantir fallbacks que preservem legibilidade.

:root {  --color-focus-ring: rgba(37, 99, 235, 0.45);  --focus-ring: 0 0 0 3px var(--color-focus-ring);}.c-button:focus-visible {  outline: none;  box-shadow: var(--focus-ring, 0 0 0 3px rgba(37, 99, 235, 0.45));}

Se um tema escuro precisar de um anel de foco diferente, ele sobrescreve --color-focus-ring sem alterar o componente.

Erros comuns na implementação e como evitar

1) Componentes consumindo primitivos diretamente

Quando um componente usa --color-blue-500 diretamente, você perde a capacidade de trocar a intenção (primário, link, destaque) sem refatorar. Prefira semânticos como --color-primary e --color-link. Se precisar de algo específico do componente, crie tokens do componente que apontem para semânticos.

2) Escalas com muitos degraus ou degraus inconsistentes

Uma escala enorme vira um “menu infinito” e incentiva escolhas arbitrárias. Uma escala pequena demais força exceções. Ajuste com base no uso real: se --space-5 nunca é usado, remova; se --space-7 é sempre improvisado, talvez falte um degrau.

3) Fallbacks que mascaram problemas

Fallback é proteção, não desculpa para tokens faltando. Se você sempre usa fallback literal, pode não perceber que um tema está incompleto. Uma prática útil é: em desenvolvimento, validar se os tokens essenciais existem (por revisão, testes visuais ou linting). No CSS em si, mantenha fallbacks para tokens críticos (cores de texto/fundo) e para componentes distribuídos, mas evite depender deles para tudo.

4) Misturar unidade e intenção no mesmo token

Evite nomes como --space-16px ou --radius-8 sem contexto. Prefira degraus coerentes (--space-4) e semânticos (--space-surface-padding). Assim, você pode mudar a unidade (px para rem) ou recalibrar a escala sem renomear tudo.

Checklist prático para uma implementação consistente

  • Declare primitivos em :root e consuma semânticos nos componentes.
  • Crie semânticos para superfícies, texto, bordas, links e ações (primário, perigo etc.).
  • Implemente temas sobrescrevendo semânticos (e só ajuste primitivos se necessário).
  • Use escalas para espaçamento e tipografia; evite valores soltos.
  • Adote fallbacks em var() para migração, distribuição e resiliência.
  • Quando precisar de variação local, sobrescreva tokens no escopo em vez de duplicar regras.
  • Considere tokens fluidos com clamp() para responsividade com menos complexidade.

Agora responda o exercício sobre o conteúdo:

Qual abordagem melhor preserva escalabilidade e facilidade de troca de tema ao aplicar design tokens em componentes CSS?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

Tokens semânticos representam intenção (ex.: fundo, texto, primário) e criam um contrato estável para componentes. Assim, o tema pode sobrescrever apenas semânticos sem exigir refatoração dos componentes, mantendo primitivos como valores de base.

Próximo capitúlo

Implementação de tokens com pré-processadores: mapas, funções e geração de utilitários

Arrow Right Icon
Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.