Do primeiro token até a governança de grande porte, aprenda a construir um sistema escalável, resiliente e focado na verdadeira adoção pelos desenvolvedores.
Recursos seleccionados para complementar tu lectura

Frontend Engineer
Engenheiro frontend especializado em interfaces rápidas, acessíveis e escaláveis. Conecta excelência técnica com experiências que os usuários realmente adoram usar. Também mentoro desenvolvedores e criadores em programas ao vivo, podcasts e iniciativas de comunidade focadas em tecnologia inclusiva.
Checklist de 47 puntos para detectar bugs, riesgos de seguridad y problemas de rendimiento antes del lanzamiento.
Continúa explorando temas similares
Templates probados en producción, usados por desarrolladores. Ahorra semanas de setup en tu próximo proyecto.
Consultorías modulares con diagnóstico técnico, plan de acción y acompañamiento directo. Desde auditorías express hasta CTO fraccionado.
2 cupos para consultorías en el Q2

São 16h47 de uma quarta-feira. O designer acabou de entregar o novo fluxo de onboarding. O desenvolvedor abre o Figma, olha os componentes, e pensa: "esse botão é parecido com o que eu já fiz, mas não é igual." Ele abre o Storybook. Encontra três variantes de botão. Nenhuma bate exatamente com o design. Ele abre o repositório. Descobre que outro time criou um quarto botão semana passada. Ninguém documentou.
Resultado? Ele cria o quinto botão.
Essa cena se repete em milhares de empresas todos os dias. É o sintoma mais visível de um problema que custa caro: a ausência de um design system funcional. Não de um design system que existe — muitos existem. Mas de um que funciona. Que é usado. Que é mantido. Que evolui.
Segundo dados da indústria, aproximadamente 70% dos design systems falham nos primeiros dois anos. Não porque faltam componentes. Não porque a equipe é incompetente. Mas porque o time investiu meses construindo o sistema perfeito — e ninguém adotou.
Este artigo é sobre a parte que ninguém fala. Não vou te ensinar a criar um botão com variantes. Vou te mostrar como construir um design system que as pessoas realmente usam. Do primeiro token até a governança enterprise. Com código, com exemplos reais, com os erros que eu vi times cometendo repetidamente.
Um design system sem adoção é apenas um Storybook abandonado.
Vamos começar pelo erro que quase todo mundo comete.
A maioria dos times começa um design system assim: "Vamos criar o Button, o Input, o Modal, o Card..." E em três meses, tem uma biblioteca de 40 componentes que ninguém usa.
Por que isso acontece?
Porque componentes sem fundação são opiniões individuais empacotadas em código. Cada desenvolvedor que criou um componente tomou dezenas de microdecisões: qual o padding? Qual a cor do hover? Qual o border-radius? Qual a fonte? Essas decisões não foram alinhadas com ninguém. São arbitrárias.
O resultado é um Frankenstein visual. Componentes que "funcionam" isoladamente mas não formam um todo coerente.
O erro fundamental é começar pela camada errada de abstração.
Pense assim: você não constrói uma casa começando pelo telhado. Você começa pela fundação. Em design systems, a fundação não são os componentes. São os design tokens.
Visualize uma pirâmide de cinco camadas. Cada camada depende da anterior:
/\
/ \ TEMPLATES
/ \ Composições de página completas
/------\
/ \ PATTERNS
/ \ Combinações recorrentes de componentes
/------------\
/ \ COMPONENTES
/ \ Elementos de UI reutilizáveis
/------------------\
| PRIMITIVOS | Elementos atômicos (tipografia, espaçamento, ícones)
|-------------------|
| DESIGN TOKENS | Valores fundamentais (cores, tamanhos, durações)
-------------------
| Camada | O que é | Exemplo | Muda com que frequência |
|---|---|---|---|
| Tokens | Valores atômicos de design | --color-primary-500: #2563EB | Raramente |
| Primitivos | Elementos base sem lógica de negócio | Tipografia, grid, espaçamento | Pouco |
| Componentes | Elementos de UI reutilizáveis | Button, Input, Card, Modal | Moderadamente |
| Patterns | Combinações recorrentes | FormField (Label + Input + Error) | Frequentemente |
| Templates | Composições de página | Layout de dashboard, página de settings | Muito frequentemente |
A maioria dos times pula direto para a camada 3 (componentes). E é por isso que falham.
Não comece pelo Button. Comece pelo motivo de o Button existir.
Design tokens são a camada mais fundamental de um design system. São pares de nome-valor que representam decisões de design: cores, espaçamentos, tipografia, sombras, durações de animação, border-radius.
Por que são tão importantes? Porque são agnósticos de plataforma. O mesmo token funciona em React, Flutter, iOS nativo, Android, e-mail marketing. E porque criam um vocabulário compartilhado entre design e desenvolvimento.
/* ========================================
TIER 1: Global Tokens (valores brutos)
======================================== */
:root {
/* Cores - Escala completa */
--global-blue-50: #EFF6FF;
--global-blue-100: #DBEAFE;
--global-blue-200: #BFDBFE;
--global-blue-300: #93C5FD;
--global-blue-400: #60A5FA;
--global-blue-500: #3B82F6;
--global-blue-600: #2563EB;
--global-blue-700: #1D4ED8;
--global-blue-800: #1E40AF;
--global-blue-900: #1E3A8A;
--global-neutral-0: #FFFFFF;
--global-neutral-50: #F9FAFB;
--global-neutral-100: #F3F4F6;
--global-neutral-200: #E5E7EB;
--global-neutral-700: #374151;
--global-neutral-800: #1F2937;
--global-neutral-900: #111827;
/* Espaçamento - Escala de 4px */
--global-space-1: 0.25rem; /* 4px */
--global-space-2: 0.5rem; /* 8px */
--global-space-3: 0.75rem; /* 12px */
--global-space-4: 1rem; /* 16px */
--global-space-6: 1.5rem; /* 24px */
--global-space-8: 2rem; /* 32px */
--global-space-12: 3rem; /* 48px */
--global-space-16: 4rem; /* 64px */
/* Tipografia */
--global-font-family-sans: 'Inter', system-ui, sans-serif;
--global-font-family-mono: 'JetBrains Mono', monospace;
--global-font-size-xs: 0.75rem;
--global-font-size-sm: 0.875rem;
--global-font-size-base: 1rem;
--global-font-size-lg: 1.125rem;
--global-font-size-xl: 1.25rem;
--global-font-size-2xl: 1.5rem;
/* Durações */
--global-duration-fast: 150ms;
--global-duration-normal: 250ms;
--global-duration-slow: 400ms;
/* Sombras */
--global-shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
--global-shadow-md: 0 4px 6px rgba(0,0,0,0.07);
--global-shadow-lg: 0 10px 15px rgba(0,0,0,0.1);
/* Border Radius */
--global-radius-sm: 0.25rem;
--global-radius-md: 0.5rem;
--global-radius-lg: 0.75rem;
--global-radius-full: 9999px;
}
/* ========================================
TIER 2: Semantic Tokens (intenção)
======================================== */
:root {
/* Cores semânticas */
--color-primary: var(--global-blue-600);
--color-primary-hover: var(--global-blue-700);
--color-primary-active: var(--global-blue-800);
--color-primary-subtle: var(--global-blue-50);
--color-surface: var(--global-neutral-0);
--color-surface-secondary: var(--global-neutral-50);
--color-border: var(--global-neutral-200);
--color-text-primary: var(--global-neutral-900);
--color-text-secondary: var(--global-neutral-700);
--color-success: #16A34A;
--color-warning: #D97706;
--color-error: #DC2626;
/* Espaçamento semântico */
--space-component-gap: var(--global-space-3);
--space-section-gap: var(--global-space-8);
--space-page-padding: var(--global-space-6);
/* Tipografia semântica */
--font-body: var(--global-font-size-base);
--font-caption: var(--global-font-size-sm);
--font-heading: var(--global-font-size-2xl);
}
/* ========================================
TIER 3: Component Tokens (específicos)
======================================== */
:root {
--button-padding-x: var(--global-space-4);
--button-padding-y: var(--global-space-2);
--button-radius: var(--global-radius-md);
--button-font-size: var(--global-font-size-sm);
--button-font-weight: 600;
--button-primary-bg: var(--color-primary);
--button-primary-bg-hover: var(--color-primary-hover);
--button-primary-text: var(--global-neutral-0);
--button-transition: all var(--global-duration-fast) ease-in-out;
}
Perceba a estrutura: Global -> Semântico -> Componente.
--global-blue-600 não diz nada sobre onde usar.--color-primary diz "use isso como cor principal".--button-primary-bg diz exatamente onde aplicar.Essa hierarquia é crucial. Quando você precisa mudar a cor primária do produto, muda em um lugar (--color-primary), e todos os componentes que referenciam esse token atualizam automaticamente. Quando você precisa de dark mode, troca os semantic tokens sem tocar nos componentes.
/* Dark mode: só troca os semantic tokens */
[data-theme="dark"] {
--color-surface: var(--global-neutral-900);
--color-surface-secondary: var(--global-neutral-800);
--color-border: var(--global-neutral-700);
--color-text-primary: var(--global-neutral-50);
--color-text-secondary: var(--global-neutral-200);
--color-primary: var(--global-blue-400);
--color-primary-hover: var(--global-blue-300);
}
/* Nenhum componente precisa mudar. Zero. */
Design tokens são o DNA do seu produto. Componentes são apenas a expressão fenotípica.
Para manter Figma e código sincronizados, use uma estrutura de tokens que espelha o código:
{
"global": {
"color": {
"blue": {
"50": { "value": "#EFF6FF", "type": "color" },
"500": { "value": "#3B82F6", "type": "color" },
"600": { "value": "#2563EB", "type": "color" },
"700": { "value": "#1D4ED8", "type": "color" }
}
},
"space": {
"1": { "value": "4", "type": "spacing" },
"2": { "value": "8", "type": "spacing" },
"4": { "value": "16", "type": "spacing" }
}
},
"semantic": {
"color": {
"primary": {
"value": "{global.color.blue.600}",
"type": "color"
},
"primary-hover": {
"value": "{global.color.blue.700}",
"type": "color"
}
}
}
}
Ferramentas como Tokens Studio (antigo Figma Tokens) e Style Dictionary permitem sincronizar essa estrutura entre Figma e código automaticamente. Isso elimina o "telefone sem fio" entre design e desenvolvimento.
Aqui está um dos conselhos mais práticos deste artigo:
Só crie um componente para o design system quando precisar dele pela terceira vez.
Por que essa regra funciona? Porque evita abstração prematura — um dos pecados capitais da engenharia de software. Quando você cria um componente na primeira vez, está adivinhando quais variações vai precisar. Na terceira vez, você sabe.
Imagine que no sprint 1, você cria um componente <DataTable> genérico com sorting, pagination, filtering, column resizing, row selection, infinite scroll e export to CSV. Você gastou duas semanas.
No sprint 3, o time de billing precisa de uma tabela. Mas a tabela deles tem células editáveis. Seu <DataTable> não suporta. Eles criam uma tabela própria.
No sprint 5, o time de analytics precisa de uma tabela. Mas com gráficos inline nas células. Criam outra tabela.
Agora você tem três tabelas no codebase. A "oficial" que ninguém usa porque é rígida demais, e duas "piratas" que resolvem problemas reais.
Se você tivesse esperado, teria visto os três use cases reais antes de criar a abstração. Teria construído um <DataTable> que realmente atende às necessidades do time.
| Abordagem | Tempo investido | Componentes no codebase | Adoção |
|---|---|---|---|
| Criar genérico no dia 1 | 2 semanas | 3 (1 oficial + 2 piratas) | Baixa |
| Regra do 3 | 1 semana (após 3 use cases) | 1 (que atende todos) | Alta |
A regra do 3: se você construiu a mesma coisa três vezes, ela merece virar componente. Antes disso, é otimização prematura.
Quando você finalmente decide criar um componente, ele precisa seguir uma anatomia consistente. Aqui está um modelo que funciona para a maioria dos times:
// Button.tsx
import { forwardRef } from 'react';
import type { ButtonProps } from './Button.types';
import styles from './Button.module.css';
import { clsx } from 'clsx';
/**
* Button - Componente primário de ação
*
* @example
* <Button variant="primary" size="md">
* Salvar alterações
* </Button>
*
* @example
* <Button variant="ghost" size="sm" leftIcon={<IconPlus />}>
* Adicionar item
* </Button>
*/
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
(
{
variant = 'primary',
size = 'md',
isLoading = false,
isDisabled = false,
leftIcon,
rightIcon,
children,
className,
...rest
},
ref
) => {
return (
<button
ref={ref}
className={clsx(
styles.button,
styles[variant],
styles[size],
isLoading && styles.loading,
className
)}
disabled={isDisabled || isLoading}
aria-busy={isLoading}
{...rest}
>
{isLoading && <span className={styles.spinner} aria-hidden="true" />}
{!isLoading && leftIcon && (
<span className={styles.icon}>{leftIcon}</span>
)}
<span className={styles.label}>{children}</span>
{!isLoading && rightIcon && (
<span className={styles.icon}>{rightIcon}</span>
)}
</button>
);
}
);
Button.displayName = 'Button';
// Button.types.ts
import type { ButtonHTMLAttributes, ReactNode } from 'react';
export interface ButtonProps
extends ButtonHTMLAttributes<HTMLButtonElement> {
/** Estilo visual do botão */
variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
/** Tamanho do botão */
size?: 'sm' | 'md' | 'lg';
/** Estado de carregamento */
isLoading?: boolean;
/** Estado desabilitado */
isDisabled?: boolean;
/** Ícone à esquerda do label */
leftIcon?: ReactNode;
/** Ícone à direita do label */
rightIcon?: ReactNode;
}
/* Button.module.css */
.button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--global-space-2);
font-family: var(--global-font-family-sans);
font-weight: var(--button-font-weight);
font-size: var(--button-font-size);
border-radius: var(--button-radius);
border: 1px solid transparent;
cursor: pointer;
transition: var(--button-transition);
white-space: nowrap;
user-select: none;
}
/* Variants */
.primary {
background: var(--button-primary-bg);
color: var(--button-primary-text);
}
.primary:hover:not(:disabled) {
background: var(--button-primary-bg-hover);
}
.secondary {
background: var(--color-surface);
color: var(--color-text-primary);
border-color: var(--color-border);
}
.secondary:hover:not(:disabled) {
background: var(--color-surface-secondary);
}
.ghost {
background: transparent;
color: var(--color-primary);
}
.ghost:hover:not(:disabled) {
background: var(--color-primary-subtle);
}
.danger {
background: var(--color-error);
color: var(--global-neutral-0);
}
/* Sizes */
.sm {
padding: var(--global-space-1) var(--global-space-3);
font-size: var(--global-font-size-xs);
}
.md {
padding: var(--button-padding-y) var(--button-padding-x);
}
.lg {
padding: var(--global-space-3) var(--global-space-6);
font-size: var(--global-font-size-lg);
}
/* States */
.loading {
opacity: 0.7;
cursor: wait;
}
.button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.button:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
Note os pontos críticos:
forwardRef — permite que o consumidor acesse o DOM node (necessário para tooltips, focus management).aria-busy, focus-visible, estados de disabled.Aqui está uma verdade desconfortável: a qualidade do seu design system é irrelevante se ninguém sabe como usá-lo.
Documentação não é o manual que ninguém lê. É o produto que todo dev consulta às 2 da manhã.
| Seção | Conteúdo | Por que importa |
|---|---|---|
| Quando usar | Cenários de uso adequados | Evita uso incorreto |
| Quando NÃO usar | Cenários onde outro componente é melhor | Evita gambiarra |
| Playground interativo | Editor ao vivo com preview | Dev testa antes de copiar |
| Props / API | Tabela completa de props com tipos e defaults | Referência rápida |
| Exemplos reais | Composições comuns (não exemplos triviais) | Cópia e cola produtivo |
| Acessibilidade | Notas de a11y, keyboard nav, screen reader | Conformidade |
| Changelog | Histórico de mudanças com breaking changes | Confiança para atualizar |
Compare estas duas documentações de um componente <Alert>:
Ruim:
## Alert
Componente de alerta. Aceita as props `variant`, `title` e `children`.
Boa:
## Alert
Use `Alert` para comunicar feedback contextual ao usuário
após uma ação ou para destacar informações importantes.
### Quando usar
- Feedback após submissão de formulário (sucesso/erro)
- Avisos sobre estado do sistema (manutenção, limites)
- Informações contextuais que o usuário precisa notar
### Quando NÃO usar
- Para notificações temporárias (use `Toast`)
- Para confirmação de ações destrutivas (use `Dialog`)
- Para status de loading (use `Skeleton` ou `Spinner`)
### Exemplos
#### Feedback de formulário
[Playground interativo com Alert variant="success"]
#### Alerta de sistema
[Playground interativo com Alert variant="warning"]
#### Composição com ação
[Playground interativo com Alert + Button de ação]
### Acessibilidade
- Usa `role="alert"` para variantes de erro e warning
- Usa `role="status"` para variantes informativas
- Botão de fechar tem `aria-label="Fechar alerta"`
- Foco é gerenciado automaticamente para alertas críticos
A diferença entre as duas é a diferença entre um DS que é adotado e um que é ignorado. Invista em documentação como você investiria em um produto — porque ela é um produto.
Um design system sem governance é um repositório com 47 contribuidores e nenhuma direção. Governance não é burocracia. É clareza sobre como decisões são tomadas.
+-----------------------+
| DS Core Team |
| (2-3 pessoas) |
| - 1 Design Engineer |
| - 1 Frontend Sr |
| - 1 Product Designer|
+-----------+-----------+
|
Aprova/rejeita propostas
|
+-----------+-----------+
| Contribuidores |
| (qualquer dev/ |
| designer do time) |
+-----------+-----------+
|
Submete propostas via RFC
|
+-----------+-----------+
| Consumidores |
| (todos os times |
| de produto) |
+-----------------------+
Antes de adicionar qualquer componente ao DS, o proponente preenche um RFC simples:
## RFC: [Nome do Componente]
### Problema
Qual problema esse componente resolve?
### Evidência de necessidade
- Onde ele já foi implementado? (links para PRs/repos)
- Quantos times precisam dele?
- Passa na regra do 3?
### API proposta
Quais props? Quais variantes? Quais estados?
### Alternativas consideradas
O que mais foi avaliado? Por que essa abordagem é melhor?
### Checklist
- [ ] Review de design (consistência visual)
- [ ] Review de acessibilidade (WCAG AA)
- [ ] Review de API (consistência com outros componentes)
- [ ] Testes (unitários + visuais)
- [ ] Documentação
Este processo parece burocracia, mas na verdade é protetor. Ele evita que o DS vire um depósito de componentes que alguém achou que seria legal ter. E o processo é rápido — um RFC pode ser aprovado em 24-48 horas para componentes simples.
Governança sem burocracia: quem decide rápido, escala rápido.
O DS precisa de versionamento previsível. Use SemVer com rigor:
| Tipo de mudança | Versão | Exemplo |
|---|---|---|
| Fix de bug, ajuste visual minor | PATCH (1.0.1) | Corrigir padding do Button em mobile |
| Novo componente, nova variante | MINOR (1.1.0) | Adicionar Alert component |
| Breaking change em API | MAJOR (2.0.0) | Renomear prop isDisabled para disabled |
Publique um changelog detalhado em cada release. Times precisam saber se podem atualizar com segurança.
Você pode ter o design system mais bonito do mundo. Se ninguém usa, ele não existe.
Adoção é o problema número um de design systems. E é um problema de pessoas, não de tecnologia. Entender a psicologia por trás da resistência é fundamental.
1. Redução da Energia de Ativação
Em física, energia de ativação é a barreira mínima para iniciar uma reação. Em adoção de DS, é a fricção para começar a usar.
Reduza essa fricção ao mínimo:
# Instalação deve ser UM comando
npm install @empresa/design-system
# Uso deve ser ÓBVIO
import { Button } from '@empresa/design-system';
Se o dev precisa configurar webpack, instalar plugins, criar wrappers, ou ler 20 páginas de documentação antes de usar o primeiro componente — você perdeu.
2. O Efeito IKEA
Pessoas valorizam desproporcionalmente coisas que ajudaram a construir. Use isso a seu favor:
Quando o dev sente que o DS é "dele também", a resistência desaparece.
3. Network Effects
O DS se torna exponencialmente mais valioso conforme mais times adotam. Mas você precisa atingir uma massa crítica primeiro.
Estratégia: comece pelo time mais influente. Se o time de produto principal adotar, os outros seguem. Não tente convencer 10 times ao mesmo tempo. Convença 1, faça funcionar, e use como caso de sucesso.
4. Switching Costs Saudáveis
Uma vez que o time está usando tokens do DS para cores, espaçamento e tipografia, mudar para uma solução própria significa refazer tudo. Esse lock-in é saudável — é o mesmo princípio que faz você continuar usando o mesmo banco.
A estratégia é: comece a adoção pelos tokens, não pelos componentes. Tokens têm fricção de adoção baixa e switching cost alto. É a porta de entrada perfeita.
5. Paradoxo da Escolha
Quando um dev precisa criar uma UI sem DS, ele toma centenas de microdecisões: qual padding? qual sombra? qual tom de cinza? Isso causa fadiga de decisão.
O DS elimina essas decisões. Faça essa mensagem explícita: "Você não precisa mais decidir qual cinza usar. Use --color-text-secondary e pronto."
O melhor design system é aquele que o dev nem percebe que está usando.
| Métrica | Sem DS | Com DS maduro |
|---|---|---|
| Tempo para criar nova tela | 3–5 dias | 0.5–1 dia |
| Variantes de botão no codebase | 12+ | 4 (controladas) |
| Inconsistências visuais por sprint | 15–20 | 1–2 |
| Tempo de onboarding (frontend) | 2–3 semanas | 3–5 dias |
| Bugs visuais em produção | Frequentes | Raros |
| Retrabalho de UI | ~30% do tempo | ~5% do tempo |
Esses números não são teóricos. São padrões observados em times que passaram pela transição de "sem DS" para "DS maduro". A diferença é dramática.
Aqui é onde as coisas ficam realmente interessantes. Com o avanço dos LLMs, design systems bem estruturados ganham um superpoder: geração de UI consistente via IA.
Quando você alimenta um LLM com seus design tokens e a API dos seus componentes, ele consegue gerar código que usa seu DS nativamente. Mas a qualidade da geração depende diretamente da qualidade da sua documentação.
## Contexto para IA (system prompt ou spec)
### Tokens disponíveis
- Cores: use variáveis CSS `--color-*` (nunca hex direto)
- Espaçamento: use `--global-space-*` (escala de 4px)
- Tipografia: use `--global-font-size-*`
### Componentes disponíveis
- `<Button variant="primary|secondary|ghost|danger" size="sm|md|lg">`
- `<Input label="string" error="string?" placeholder="string?">`
- `<Card padding="sm|md|lg">` com composição livre
- `<Alert variant="info|success|warning|error" title="string">`
### Regras de composição
- Formulários: sempre Label acima do Input, erro abaixo
- Ações primárias: máximo 1 por seção, sempre à direita
- Espaçamento entre seções: use `--space-section-gap`
- Espaçamento entre elementos: use `--space-component-gap`
Com esse contexto, um LLM consegue gerar telas inteiras que parecem ter sido construídas por alguém que conhece seu DS. Sem contexto, ele gera CSS arbitrário e componentes que não existem no seu sistema.
Design System --> Tokens + Specs documentados
| |
| IA gera UI consistente
| |
v v
Adoção aumenta <-- Devs veem valor na geração
| |
v v
Mais componentes --> Mais contexto para IA
Esse ciclo é o futuro do desenvolvimento de UI. Times que têm design systems bem documentados vão ter uma vantagem desproporcional na era da IA generativa, porque seus LLMs produzem código que já está alinhado com os padrões do time.
Dado o contexto do DS, um prompt como:
Crie uma página de settings com:
- Seção de perfil (avatar, nome, e-mail)
- Seção de notificações (toggles para e-mail, push, SMS)
- Seção de segurança (mudar senha, 2FA)
- Botão de salvar no final
Gera código que usa <Card>, <Input>, <Button>, <Toggle> e os tokens corretos de espaçamento. Sem DS, o mesmo prompt gera código com Tailwind arbitrário, componentes inventados e inconsistências visuais.
Se você não mede, não gerencia. Design systems precisam de métricas claras para justificar o investimento e guiar a evolução.
| Categoria | Métrica | Como medir | Meta |
|---|---|---|---|
| Adoção | % de componentes do DS vs custom | AST analysis, lint rules | >80% |
| Adoção | N. de times usando o DS | Package analytics | 100% dos times frontend |
| Cobertura | % de telas que usam apenas DS | Auditoria visual periódica | >90% |
| Qualidade | N. de bugs reportados no DS | Issue tracker | Decrescente |
| Velocidade | Tempo médio para criar nova tela | Sprint metrics | Decrescente |
| Contribuição | N. de PRs de fora do core team | GitHub analytics | Crescente |
| Satisfação | NPS do DS (survey trimestral) | Survey | >8/10 |
Uma forma prática de medir adoção é criar ESLint rules que detectam violações:
// eslint-plugin-design-system/rules/no-hardcoded-colors.js
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'Impede cores hardcoded. Use design tokens.',
},
messages: {
noHardcodedColor:
'Cor hardcoded detectada: "{{ value }}". Use um design token como var(--color-*).',
},
},
create(context) {
return {
Literal(node) {
if (typeof node.value === 'string') {
const hexPattern = /^#([0-9A-Fa-f]{3,8})$/;
const rgbPattern = /^rgba?\(/;
if (
hexPattern.test(node.value) ||
rgbPattern.test(node.value)
) {
context.report({
node,
messageId: 'noHardcodedColor',
data: { value: node.value },
});
}
}
},
};
},
};
Essa lint rule não bloqueia o build (comece com warn, não error). Mas cria visibilidade. O time vê quantas violações existem e pode trackear a redução ao longo do tempo.
Depois de ver dezenas de design systems em diferentes estágios de maturidade, estes são os anti-patterns mais comuns:
Criar um componente que tenta fazer tudo. O <Card> que aceita 47 props e tem 15 variantes. Quando um componente precisa de um manual, ele está fazendo coisas demais.
Solução: composição sobre configuração. Em vez de um Card com props para header, footer, actions, media, badge e collapse — crie <Card>, <CardHeader>, <CardBody>, <CardFooter> e deixe o consumidor compor.
Wrappear componentes de bibliotecas externas (como Radix, Headless UI) sem adicionar valor. Se seu <DSDialog> é apenas um <RadixDialog> com cores aplicadas, você não precisa de um wrapper. Use os tokens diretamente.
Quando wrapper faz sentido: quando você precisa de API simplificada, comportamento customizado ou isolamento para troca futura da dependência.
Construir o DS em isolamento, sem consultar os times consumidores, e apresentar como produto pronto. Resultado garantido: rejeição.
Solução: construa com os times, não para os times. Efeito IKEA em ação.
Criar tokens para absolutamente tudo. --button-icon-left-margin-hover-focus-active-sm. Quando a árvore de tokens é mais complexa que o CSS que ela substitui, você errou.
Solução: tokens para decisões de design recorrentes. Se um valor aparece em apenas um lugar, ele não precisa ser um token.
Nunca fazer breaking changes por medo de incomodar os consumidores. Resultado: API acumula dívida técnica, props deprecated que nunca somem, inconsistências que nunca são corrigidas.
Solução: breaking changes planejadas e comunicadas. Use codemods para automatizar migrações. Mantenha a versão anterior por um período de transição.
Design system que existe no Figma mas não tem implementação em código. Ou pior: a implementação não bate com o Figma.
Solução: source of truth única. Ou o Figma gera código, ou o código gera Figma. Nunca os dois vivendo vidas independentes.
Você não vai construir um design system enterprise em um sprint. É um investimento progressivo. Aqui está um roadmap realista:
Objetivo: Criar a base que tudo referencia.
Entregável: @empresa/tokens publicado no NPM interno.
Métrica de sucesso: Pelo menos 2 times usando os tokens.
Objetivo: Criar os building blocks básicos.
Entregável: @empresa/primitives publicado.
Métrica de sucesso: Novos times usam os primitivos em novos projetos.
Objetivo: Criar os componentes que 80% dos times precisam.
Entregável: @empresa/components publicado com documentação interativa.
Métrica de sucesso: >50% das novas features usam componentes do DS.
Objetivo: Escalar adoção e criar padrões de alto nível.
Entregável: Adoção mensurável e processo de contribuição funcionando.
Métrica de sucesso: >80% das novas features usam DS. PRs de fora do core team.
Objetivo: Maturidade e sustentabilidade.
Entregável: DS autossustentável com adoção >90%.
Métrica de sucesso: DS é o padrão, não a exceção.
DESIGN SYSTEM HEALTH CHECK
===========================
FUNDAÇÃO
[ ] Tokens definidos (cores, espaçamento, tipografia, sombras)
[ ] Hierarquia de tokens: Global -> Semântico -> Componente
[ ] Tokens sincronizados entre Figma e código
[ ] Versionamento semântico (SemVer)
COMPONENTES
[ ] Regra do 3 aplicada (só abstrai após 3 use cases)
[ ] Acessibilidade (WCAG AA mínimo)
[ ] Tipagem forte (TypeScript)
[ ] forwardRef em todos os componentes
[ ] Composição sobre configuração
DOCUMENTAÇÃO
[ ] Quando usar / Quando NÃO usar
[ ] Playground interativo (Storybook)
[ ] Exemplos reais (não triviais)
[ ] API reference completa
[ ] Changelog por versão
GOVERNANCE
[ ] Core team definido (2-3 pessoas)
[ ] Processo RFC para novos componentes
[ ] Criterios claros de aprovação
[ ] Contribution guidelines públicas
ADOÇÃO
[ ] Instalação em 1 comando
[ ] Fricção mínima para primeiro uso
[ ] Lint rules (warn, não error) para tracking
[ ] Canal de comunicação ativo (Slack/Discord)
[ ] Contribuição aberta a todos os times
MÉTRICAS
[ ] % de adoção medida (componentes DS vs custom)
[ ] Tempo de criação de tela trackado
[ ] NPS do DS medido trimestralmente
[ ] Número de contribuições externas trackado
IA-READY
[ ] Tokens exportados em formato legível por LLM
[ ] API de componentes documentada para contexto de IA
[ ] Regras de composição documentadas
[ ] Exemplos de prompt com contexto do DS
Você não precisa de um design system perfeito. Você precisa de um design system que o time usa, evolui e chama de seu. Comece pelos tokens. Aplique a regra do 3. Documente como produto. Governe sem burocracia. Meça tudo. E lembre-se: o melhor design system é aquele que o dev nem percebe que está usando. Comece hoje — um token de cada vez.