Design System — Tokens (fonte única da verdade)¶
Reconciliado em 2026-07-25 a partir de
impeccable.md(intenção) e do tema MUI emapps/web/src/contexts/color-mode/(implementado), que divergiam. Estas são as decisões definitivas — o app e o Figma devem convergir para cá.Onde os valores vivem em código (spec 0017): as paletas e os tokens
theme.garantora.*estão emapps/web/src/design-system/theme/palette.ts, e os mesmos valores em CSS ficam ao lado, emdesign-system/tokens/*.css. Este documento é a prosa canônica; opalette.tsé o que roda. Mudou aqui, mude nos dois — enquanto os três existirem, essa é a duplicação que sobrou, e é deliberada.Até a 0017 havia um quarto lugar: o
README.mddo pacote@garantora/design-system, uma reconstrução paralela feita a partir dos SVGs do Figma. O pacote foi internalizado e o repositório, arquivado — esse rival deixou de existir.
Decisões da reconciliação¶
| Tema | Resolução |
|---|---|
| Papel das cores | Azul = estrutura/ação primária · Teal = marca/acento · Âmbar = destaque/CTA (reflete o uso real no login) |
| Tipografia | Onest (display/títulos) + Inter (corpo) — como o app carrega hoje |
| Neutros | Tingidos na marca (azulado): dark #0F172A/#1E293B, texto light #1a1a2e/#5f6878 |
1. Cores¶
Marca (semântica)¶
| Token | Papel | Light | Dark (contraste) |
|---|---|---|---|
brand.structure | ação primária, nav ativa, botões principais | #005577 | #4DA6CC |
brand.structure-hover | hover do primário | #00445C | #3D95BB |
brand.accent | identidade, links, acentos | #008B8B | #33B3B3 |
brand.accent-hover | hover do acento | #007A7A | #2CA0A0 |
brand.highlight | CTA de conversão ("criar conta") | #FFA94D | #FFB866 |
brand.highlight-light | ouro do logo; pendência que espera o usuário | #FFCB53 | #FFCB53 |
brand.on-highlight | texto sobre âmbar | #1A1A1A | #1A1A1A |
brand.info | ciano da assinatura; dado e informação neutra | #00ACF0 | #00ACF0 |
As cinco cores da marca, e quando usar cada uma¶
O logo é um swoosh de três braços mais a assinatura. Até a spec 0019 a paleta adotava três dos cinco matizes — faltavam o ciano, que é o começo do gradiente da assinatura inteira (#00ACF0 → #005577) e aparecia uma vez cravado no login, e o ouro, primeira parada do braço laranja.
| Cor | No logo | Token | Emprego |
|---|---|---|---|
#FFA94D laranja | braço de cima | highlight | a ação mais desejada da tela |
#FFCB53 ouro | início do braço laranja | highlight.light | pendência que espera o usuário |
#008B8B teal | braço do meio | accent | marca e navegação — onde o produto se identifica |
#00ACF0 ciano | início da assinatura | info | dado e informação neutra |
#005577 navy | fim da assinatura | structure | estrutura: cabeçalho, rótulo, moldura |
Sem esta tabela, "usar mais cor" vira decisão de gosto a cada tela — foi assim que o laranja chegou a cinco usos no app inteiro enquanto o neutro somava 386.
Nenhuma cor viva serve como texto no tema claro. Sobre painel branco o teal mede 4,15:1, o ciano 2,57:1, o laranja 1,90:1, o ouro 1,51:1. Texto e glifo coloridos usam a tinta correspondente (
*-ink), sempre. É o que impede "adotar a marca" de virar "piorar o contraste".
Feedback (semântica)¶
| Token | Uso | Light | Dark (clareado p/ contraste) |
|---|---|---|---|
feedback.success | aprovação, contrato ativo | #2E7D32 | #5BBE7A |
feedback.warning | risco moderado, pendência | #F59E0B | #F7B84D |
feedback.error | inadimplência, falha, inválido | #D32F2F | #F0726F |
feedback.info | dado, aviso neutro (severidade padrão) | #00ACF0 | #00ACF0 |
Cada semântica tem cinco peças, e é preciso as cinco (spec 0019): cor viva, tinta de texto (*-ink), cor sobre preenchimento sólido (on-*), fundo de alerta e fundo de chip. Até a 0019 faltavam warning-ink, info-ink, on-warning, on-info e o chip de info — e era nas lacunas que o app improvisava: a taxa de inadimplência do Centro de Operações era pintada com o âmbar vivo, 2,15:1, o número que avisa que algo vai mal sendo o mais difícil de ler da tela.
| Tinta (light) | Valor | Medido no chip |
|---|---|---|
success-ink | #1B5E20 | 6,54:1 |
warning-ink | #AE5700 | 4,53:1 |
danger-ink | #B71C1C | 5,32:1 |
info-ink | #0071B5 | 4,53:1 |
As tintas medem contra o chip a 14% — o pior fundo em que aparecem —, não contra o painel: calibrar no painel deixa a tinta confortável solta e apertada dentro do chip. on-warning e on-info são escuros (#0F172A), porque branco sobre âmbar mede 2,15:1 e sobre ciano 2,57:1.
No dark, o feedback usa tons mais claros (menos croma) para manter contraste sobre
#0F172A/#1E293B— os valores light seriam escuros demais. No Figma são os primitivossuccess/400,warning/400,danger/400, ligados só ao modo Dark.
Neutros — Light¶
| Token | Valor | Medido |
|---|---|---|
bg.default | #EDF1F6 | — |
bg.surface (paper) | #FFFFFF | 1,13:1 contra o fundo |
text.primary | #1A1A2E | 17,06:1 |
text.secondary | #465264 | 7,92:1 (AAA) |
text.disabled | #67768B | 4,62:1 — só controle inativo |
border.field (borda de campo) | #8996AA | 3,00:1 |
border.subtle (filete de painel) | #ADB8C8 | 2,01:1 |
border.list (linha entre itens) | #C9D4E0 | 1,50:1 |
O painel é branco puro desde a spec 0019. Antes era
#FBFCFEe o fundo#F1F5F9, que diferiam 1,07:1 — quase indistinguíveis. Separar as duas superfícies escurecendo o fundo era a saída óbvia e a errada: derrubaria o contraste de todo texto que assenta nele. Clarear o painel sobe o contraste de tudo que está dentro dele, que é onde vive a maior parte do conteúdo.
text.secondarymira AAA (7:1), não AA. Os 199 usos dele no app estão todos emcaption(12px) ebody2(13px): é o corpo do conteúdo, em corpo pequeno, lido oito horas por dia. A tolerância da WCAG a contraste menor pressupõe texto grande — aqui vale o contrário.neutralInktem o mesmo valor: um conceito, um token.Separador tem três réguas, e só uma vem da norma. A WCAG 1.4.11 pede 3:1 para "informação necessária para identificar componentes". A palavra que decide é identificar:
Nível Onde Razão Por quê Campo input,select,textarea3:1 a borda é o que diz "aqui se digita" — sem ela o campo some. Exemplo canônico da norma. Painel card, diálogo, cabeçalho de tabela 2:1 o painel é identificado pelo cabeçalho e pelo conteúdo; a borda decora Lista linha entre itens 1,5:1 separa leitura, não delimita componente Onde cada nível se aplica (spec 0021): a régua segue a função na tela, não o componente que o DOM usa. O cartão que se repete por registro numa lista é a linha daquela lista — vai em
border.list, igual à célula doDataGridno desktop. UmFormPanelou um bloco de detalhe é painel, e vai emborder.subtle. Ler a estrutura em vez da função foi o que deixou a mesma lista com pesos diferentes nos dois viewports.A barra do topo não é painel. Filete embaixo, sem raio, nos dois viewports — é divisão entre áreas, não moldura de objeto. O mesmo vale para
Popover,Menu,DialogeDrawer, que já se separam do fundo por sombra.A spec 0019 aplicou 3:1 aos três e o fix 0004 corrigiu: o contorno virava o elemento mais forte da tela. Pior, a fronteira do painel passava a ser dita três vezes — superfície branca, cabeçalho tingido e borda escura —, e redundância em interface lê-se como ruído.
A régua decorativa pode ser leve porque não trabalha sozinha. Antes da 0019 o divisor a 1,20:1 sumia porque nada mais separava: painel e fundo diferiam 1,07:1 e não havia zebra perceptível. Estrutura vem de superfície e espaço; linha é apoio.
Neutros — Dark (tingidos na marca)¶
| Token | Valor | Medido |
|---|---|---|
bg.default | #0B1220 | 1,28:1 contra o painel |
bg.surface | #1E293B | — |
text.primary | #E8EAF0 | 12,16:1 |
text.secondary | #ACB4C0 | 7,00:1 (AAA) |
text.disabled | #8290A4 | 4,51:1 |
border.field (borda de campo) | #647387 | 3,03:1 |
border.subtle (filete de painel) | #49586C | 2,02:1 |
border.list (linha entre itens) | #364559 | 1,50:1 |
O
text.secondarydo escuro erargba(232,234,240,0.70)nesta tabela e#9AA4B2no código — divergência de origem, resolvida na spec 0019 em favor de um valor opaco. Cor de texto por transparência é o que faz o contraste escapar do controle da paleta.Regra: ao clarear rumo ao branco/preto, reduzir croma (evitar cor saturada nos extremos). Neutros sempre com leve tinta azulada da marca, nunca cinza puro nem
#000/#fffcravados.
2. Tipografia¶
- Display / títulos:
Onest— pesos 400, 500, 600, 700 - Corpo:
Inter— pesos 400, 600, 700 - Carregadas via Google Fonts (já em
apps/web/index.html).
Escala fixa em rem (UI de produto, não fluida), razão ~1.2–1.25:
| Token | Tamanho | Peso | Line-height | Uso |
|---|---|---|---|---|
display | 30px | Onest 700 | 1.15 | headline de marca (login) |
h1 | 24px | Onest 700 | 1.2 | título de página |
h2 | 20px | Onest 600 | 1.25 | seção |
h3 | 16px | Onest 600 | 1.3 | subtítulo / card |
body-lg | 15px | Inter 400 | 1.55 | texto de destaque |
body | 14px | Inter 400 | 1.55 | corpo padrão |
body-sm | 13px | Inter 400 | 1.5 | apoio |
caption | 12px | Inter 400 | 1.45 | metadados |
overline | 11px | Inter 600 | 1.4 | section labels (caixa-alta, letter-spacing: 0.09em) |
Corpo com
max-width~65–75ch. Em texto claro sobre fundo escuro, somar ~0.05–0.1 à line-height.
3. Espaçamento (escala 4pt)¶
Tokens semânticos, não pixel-nomeados:
| Token | px |
|---|---|
space.2xs | 4 |
space.xs | 8 |
space.sm | 12 |
space.md | 16 |
space.lg | 24 |
space.xl | 32 |
space.2xl | 48 |
space.3xl | 64 |
space.4xl | 96 |
Usar gap para espaçar irmãos; variar o espaçamento para criar hierarquia.
4. Raio¶
| Token | px |
|---|---|
radius.sm | 4 |
radius.md | 6 (default — shape.borderRadius do tema) |
radius.lg | 12 |
radius.pill | 999 |
5. Elevação (sutil, tingida)¶
O app evita sombras genéricas. Duas elevações discretas, com tinta azul-profundo:
| Token | Valor |
|---|---|
elevation.card | 0 1px 2px rgba(0,46,66,0.04) |
elevation.raised | 0 1px 2px rgba(0,46,66,0.04), 0 8px 32px rgba(0,46,66,0.08) |
Sem glassmorphism, sem sombras difusas decorativas.
6. Componentes-base (inventário para o Figma)¶
Primeira passada de componentes (com variantes e estados):
- Button —
primary(azul),secondary/outlined(teal),text/link (teal),cta(âmbar). Estados: default, hover, disabled, loading. - Text field (MUI
TextFieldsmall) — default, focus, error (comhelperText), disabled. Label comshrinkcontrolado. - Panel / Card (padrão
FormPanel) — cabeçalho comoverline+ corpo. Sem stripe lateral, sem sombra genérica. - Alert —
error/warning/success/info, com lista de detalhes opcional (padrão do form de imobiliária). - Status chip — mapeado às semânticas: aprovado (success), pendente (warning), inadimplente (error), rascunho (neutro).
- Sidebar item (do
custom-sider) — default, ativo (azul), com badge de contagem, colapsado (só ícone). - Toast — notificação não-bloqueante, auto-dismiss, canto inferior:
sucessoeinfo. Badge de ícone na cor cheia da severidade + glifo branco + título + mensagem + fechar. Passageiro, sem ação de confirmação. - Modal — dois usos, cada um uma variante (
Tipo), rodapé reusando o Button: Confirmação(warning) — ação que precisa de confirmação (Cancelar / Continuar).Erro(danger) — falha com bloco "Detalhes técnicos" (código, referência, quando, mensagem) + Copiar para o suporte; casa com oApiErrordo front (code,status,message,details[]).
Roteamento de feedback (qual componente para cada caso)¶
| Situação | Componente |
|---|---|
| Sucesso / info (confirmação passageira) | Toast — não-bloqueante, auto-dismiss |
| Ação que exige confirmação | Modal Confirmação (warning) |
| Falha de operação (com detalhe p/ suporte) | Modal Erro (danger) |
| Validação de formulário | inline no campo (Text Field error) + scroll ao 1º erro — nunca toast/modal |
| Aviso persistente no contexto | Alert — fica na página, inline |
Estruturar no Figma como variáveis (cores light/dark, tipo, espaço, raio) + componentes com variantes, numa página Design System.