Na Wonderlabs eu construí a biblioteca de componentes reutilizáveis do time e a base de design system por baixo dela, para um PWA de cartão de crédito atendendo mais de 50.000 usuários, com um time de front-end de três pessoas. Caímos na armadilha que pega a maioria dos times. Começamos pelos componentes. Parece produtivo. Você está projetando botões, cards, modais e entregando artefatos. Mas sem tokens por baixo, esses componentes são construídos sobre areia.
Por Que Começar pelos Componentes Custa Caro Depois
Na Wonderlabs, projetamos componentes por meses antes de formalizar tokens. Quando alinhamos a estrutura de tokens, tivemos que auditar cada componente e substituir valores hardcoded. Consumiu mais de um sprint e introduziu regressões. Isso não é um risco hipotético. É o que acontece.
O problema é que quando você projeta um botão antes de definir seus tokens de cor, está tomando decisões que parecem decisões de design mas são na verdade decisões de dados. Você escolhe um azul para a ação primária. Qual azul? Como se relaciona com a cor da marca? De que é derivado o estado hover? O que muda no modo escuro? Sem tokens, esses são valores arbitrários embutidos no componente. Quando a marca é atualizada ou um segundo produto precisa do sistema, você não está atualizando um token. Está caçando por cada componente em busca de valores hardcoded.
A Ordem Correta
- 1. Design tokens: escalas de cor brutas, escalas de tipografia, escala de espaçamento, border radii, elevação, motion. Esses são seus valores primitivos.
- 2. Tokens semânticos: mapeiam tokens brutos para intenção. --color-brand-blue-500 se torna --color-action-default. Essa camada lida com temas e modo escuro.
- 3. Primitivas: estilos base do Figma e CSS custom properties Angular derivadas de tokens semânticos. Nenhum componente ainda.
- 4. Componentes base: Button, Input, Badge, Tag. Cada um referencia apenas tokens. Sem valores hardcoded.
- 5. Componentes compostos: DataTable, FormGroup, NavigationBar construídos a partir de componentes base.
- 6. Templates de página: padrões de layout usados em todo o produto.
Arquitetura de Tokens: JSON como Fonte Única da Verdade
A decisão técnica mais importante em um design system é manter os tokens do Figma e os tokens de código sincronizados. A abordagem que funciona é um arquivo JSON de tokens, versionado no repositório, que o código trata como fonte única da verdade. Um build step como o Style Dictionary transforma esse JSON em CSS custom properties para Angular, constantes Swift para iOS, ou qualquer outra coisa que a plataforma precise. Como esse JSON chega lá é uma escolha com duas respostas. Escreva no código e sincronize para o Figma com um plugin como o Tokens Studio, ou escreva no Figma Variables e exporte. Escolha uma direção e deixe isso registrado, porque um time que nunca escolhe acaba com duas fontes que divergiram e nenhum jeito de dizer qual está certa. Na Wonderlabs eu segui o segundo caminho, nos meus últimos meses lá em 2023, quando o Figma Variables já existia: tokens definidos no Figma Variables, exportados como JSON, transformados com o Style Dictionary em CSS custom properties consumidas pelos nossos componentes Angular, que ficavam em cima do Angular Material. O Storybook entrou só um pouco, perto do fim.
Decida o formato do arquivo de tokens antes de escrever o primeiro token, porque as duas formas em circulação não se misturam dentro de um mesmo arquivo. O exemplo abaixo é Style Dictionary v3, onde cada token é um objeto com uma chave value. O Style Dictionary v4 e o DTCG Design Tokens Format Module usam $value e $type no lugar. Eu usei v3 na Wonderlabs e o snippet reflete isso. Em um projeto novo hoje eu escreveria no formato DTCG, porque é para lá que o tooling está indo, e depois checaria se cada plugin e cada build step da cadeia realmente lê esse formato.
{
"color": {
"brand": {
"blue": {
"50": { "value": "#EFF6FF" },
"100": { "value": "#DBEAFE" },
"500": { "value": "#3B82F6" },
"700": { "value": "#1D4ED8" },
"900": { "value": "#1E3A8A" }
}
},
"action": {
"default": { "value": "{color.brand.blue.500}" },
"hover": { "value": "{color.brand.blue.700}" },
"disabled": { "value": "{color.brand.blue.100}" }
}
},
"spacing": {
"xs": { "value": "4px" },
"sm": { "value": "8px" },
"md": { "value": "16px" },
"lg": { "value": "24px" },
"xl": { "value": "32px" },
"2xl": { "value": "48px" }
}
}Governança Faz Parte do Sistema, Não É um Problema Para Depois
Um design system sem um processo de contribuição se torna um depósito de lixo em poucos meses. Defina cedo quem pode adicionar tokens, quem pode adicionar componentes e qual é o processo de revisão. Mesmo em um time de front-end de três pessoas isso não é opcional. Um componente que é entregue com um padrão de acessibilidade quebrado é usado em muitas features antes do bug aparecer. Um workflow baseado em PR com um designer e um desenvolvedor como revisores obrigatórios pega a maioria dos problemas antes de se propagarem.
Versionamento importa mais do que a maioria dos times espera quando começam. Quando você muda a API de um componente ou renomeia um token, os times consumidores precisam de tempo para migrar. A gente nunca passou por isso na Wonderlabs. Três pessoas, um produto, e a biblioteca era entregue dentro do app, então havia exatamente um consumidor e uma renomeação era um commit só. No dia em que um segundo produto adota a biblioteca, isso para de funcionar. Essa é a parte que eu montaria desde o começo na próxima vez: a biblioteca publicada como um pacote npm com escopo, versionamento semântico em cada release, e schematics Angular para as migrações que quebram. Sem isso, os times ficam em versões antigas indefinidamente e você acaba mantendo duas versões do sistema.
Um design system é um produto, não um projeto. Ele precisa de manutenção, versionamento, documentação e um time que seja dono dele. Trate-o como uma construção única e ele vai apodrecer em um ano.
Integração com Angular: CSS Custom Properties e Overrides de Tema
O formato que eu daria a ele na próxima vez, para Angular: o design system publicado como um pacote npm com escopo e consumido através de Angular libraries em um monorepo. Componentes expõem seus estilos através de CSS custom properties mapeadas a partir do arquivo de tokens. Apps consumidoras aplicam tema sobrescrevendo essas custom properties no nível do host, nunca modificando código de componente. Essa é a arquitetura que torna multi-marca viável. Uma segunda marca vira um segundo conjunto de valores de token aplicado no host, não um fork do código dos componentes.
Se eu começasse aquele sistema de novo, tokens seriam a primeira entrega. Antes de existir um único componente no Figma, a escala de cores, o mapeamento de tokens semânticos e a escala de espaçamento iriam para um arquivo de tokens revisado. Cada decisão de componente feita depois disso teria uma base para referenciar. O retrabalho que você evita vale muito mais do que o tempo extra gasto em tokens no início.
Referências
- Style Dictionary. https://styledictionary.com/
- DTCG Design Tokens Format Module. https://www.designtokens.org/tr/2025.10/format/
- Tokens Studio. https://tokens.studio/
- Figma Variables. https://help.figma.com/hc/en-us/articles/15339657135383-Guide-to-variables-in-Figma
- Semantic versioning. https://semver.org/
- Angular schematics. https://angular.dev/tools/cli/schematics