Saldo projetado. Peça-chave do core da tese (UC-02).
Abrir no Storybook ↗Quando usar
- Quando o produto precisa exibir saldo projetado ou atual num horizonte temporal com delta comparativo opcional — a peça central do fluxo de viabilidade (UC-02).
- Dashboard financeiro como card principal de orientação: o usuário abre o app e precisa saber de onde está antes de qualquer ação.
- Primeiro bloco da resposta de viabilidade — UC-02: "Dá pra fazer, mas aperta. Saldo projetado em 15/dez: R$ 1.200 após viagem e compromissos fixos."
- Hero de saldo quando a tela tem saldo como dado dominante (ex.: tela de planejamento de viagem, simulação de antecipação).
Quando não usar
- Para detalhar a composição do saldo (quais lançamentos, quais categorias) — usar listagem de lançamentos. O Balance Card entrega o número; a listagem explica de onde ele veio.
- Para valor único sem contexto temporal (ex.: "você tem R$ 500 na conta") — usar texto simples em serif dentro de um contexto que já tem estrutura. O Balance Card sem
horizonperde o dado que justifica a peça. - Em superfícies de dívida, inadimplência ou risco alto — nessas cenas o card é substituído por bloco funcional de estado crítico em V-A puro. Nenhum saldo projetado alegre quando a situação é grave.
Anatomia
Cinco slots, dois obrigatórios (kicker e value). Os slots horizon, delta e caption são opcionais mas altamente recomendados — sem horizon o valor fica sem âncora temporal.
Decisão de markup: composição de classes. O Balance Card usaclassName="peppe-card peppe-balance-card" — compõe a classe base do Card com o modificador semântico do Balance Card. Essa abordagem herda o CSS do primitivo (raised, --radius-xl quando é card-mãe, sombra, border gradient) e adiciona apenas o layout e a tipografia específicos. Evita duplicação de tokens de superfície e mantém claro que o Balance Card é um Card com semântica adicional — alinhado com o princípio de economia de vocabulário (components.md §7.1).
Raio padrão: --radius-xl (32px). O Balance Card é card-mãe do dashboard na maioria das cenas — portanto usa o raio mais aberto da escala, sinalizando hierarquia. Quando encaixado dentro de outro card ou usada como hero de seção, o modificador peppe-card--hero (--radius-lg, 24px) pode ser aplicado junto.
Variantes
Uma variante por enquanto. O Balance Card v1 tem variante única — a composition base com os cinco slots. Variações de layout (horizontal value + delta; valor em faixa estreita; modo compacto para mobile) virão com surface templates no ciclo seguinte, quando o padrão de uso real em produto estiver validado.
R$ 1.200
em 15/dez
após viagem e compromissos fixos
Variantes por canal. app: card visual com value em display serif (default). voz: locução linear ("Saldo projetado em 15 de dezembro: mil e duzentos reais; duzentos e trinta acima da média"). whatsapp: bloco com kicker em caps + value em texto reforçado + horizon e delta em linhas separadas. e-mail: card HTML fallback com tabela inline e fonte sistema em caso de fallback sem Fraunces. sms: formato ultra-compacto em 1 linha — "SALDO 15/dez: R$ 1.200 (+R$ 230)".
Estados
default
R$ 1.200
em 15/dez
após viagem e compromissos fixos
Cópia canônica do UC-02. Kicker + value em display serif + horizon + Chip success + caption do Peppe.
loading
Kicker e horizon em linhas xs/sm; value vira bloco alto preservando a dimensão do display serif. Caption em linha md. Sem texto — leitor de tela recebe aria-busy="true".
erro
Não consegui carregar o saldo agora. Tenta de novo?
Bloco funcional com ícone de erro, texto canônico e ação de retry. role="alert" para leitores de tela. Escala para SystemError template quando necessário.
vazio
Sem histórico ainda. Conecta sua conta pra eu acompanhar.
EmptyState propositivo. A tela vazia vira convite. Sem lamento, sem desculpa — segue voice-and-tone.md §3.1 (estado vazio: leve e propositivo).
Tokens
O Balance Card herda os tokens de superfície do Card (raised) e adiciona os papéis tipográficos específicos dos seus slots.
| Token | Valor | Papel |
|---|---|---|
| Herdados do Card | ||
--color-surface-raised-top/bottom | #F7F7F7 → #FBFBFB | Gradiente de fundo raised |
--border-raised-top/bottom | #FCFCFC → #F8F8F8 | Border gradient direcional |
--shadow-raised | — | Sombra direcional canto sup-esq |
--radius-xl | 32px | Raio card-mãe (padrão do Balance Card) |
--space-32 | 32px | Padding interno (herda de mother) |
| Slots do Balance Card | ||
--type-family-sans | Instrument Sans | kicker, horizon, caption |
--type-family-serif | Fraunces | value — ≥ 32px (limiar: visual-language.md §4.4) |
--type-size-xs | 12px | kicker — caps medium tracking-loose |
--type-size-display | 64px | value — serif tracking-tight |
--type-size-sm | 14px | horizon, caption |
--type-tracking-loose | 0.08em | kicker (uppercase tracking) |
--type-tracking-tight | -0.02em | value (display serif) |
--color-ink-primary | #171717 | value — contraste AA sobre raised |
--color-ink-tertiary | #A1A1A1 | kicker |
--color-ink-secondary | #6B6B6B | horizon, caption |
--space-4 | 4px | gap kicker → value (ajuste óptico) |
--space-8 | 8px | gap value → horizon, gap horizon → delta |
--space-16 | 16px | gap delta → caption |
| Delta — Chip herdado | ||
--color-functional-success / -tint | #37A35A / #E8F2EC | delta positivo |
--color-functional-alert / -tint | #D4A017 / #F6EEDA | delta negativo |
Nota sobre serif no value. O token --type-family-serif (Fraunces, placeholder de sabor retrô-70s) é aplicado ao .peppe-balance-card__value porque o tamanho resultante é 64px — bem acima do limiar de 32px definido em visual-language.md §4.4. Em breakpoints menores, --type-size-display cai para 56px (tablet+mobile) — ainda acima do limiar. A serif nunca desce de 32px neste componente.
Conteúdo
kicker é a etiqueta de categoria — "SALDO PROJETADO" ou "SALDO ATUAL". Caps, máximo 2–3 palavras. Zero pontuação final. Nunca "SALDO DO MÊS DE DEZEMBRO DE 2026" — o kicker posiciona, não descreve.
value é sempre BRL com separador de milhar e decimais opcionais: "R$ 1.200" (sem decimais quando o arredondamento é explícito) ou "R$ 1.200,00" (quando a precisão é relevante). Ponto como separador de milhar, vírgula como decimal — locale PT-BR. Nunca "1200" sem formatação; nunca "BRL 1200".
horizon é o âncora temporal — curto e direto: "em 15/dez", "hoje", "fim do mês", "em 30 dias". Sem "no dia quinze de dezembro". O usuário lê em trânsito.
delta é o Chip de comparativo com tone coerente: success para delta positivo (acima da média, crescimento), alert para delta negativo (abaixo, queda). A cor do Chip não é decorativa — é sinal funcional. Exemplos: "+R$ 230 vs. média", "−R$ 140 vs. mês anterior".
caption é o Peppe amarrando o contexto — a frase que justifica o número: "após viagem e compromissos fixos", "incluindo parcelas de dezembro". Máximo 1 linha. Referência canônica: content-design/examples.md §6 — "Dá pra fazer, mas aperta. Saldo projetado em 15/dez: R$ 1.200 após viagem e compromissos fixos."
Fallback SMS: "SALDO 15/dez: R$ 1.200" — valor + horizonte, sem formatação rica. Máximo 160 caracteres.
Acessibilidade
aria-labelcomposto no container. O<article>recebearia-labelque sumariza todos os slots presentes:"saldo projetado em 15/dez: R$ 1.200. +R$ 230 vs. média. Após viagem e compromissos fixos."— o leitor de tela lê o bloco inteiro sem precisar navegar pelos filhos.- Valor em ink-primary sobre raised. #171717 sobre #F7F7F7 — contraste ~17:1, passa WCAG AAA. A serif em 64px não tem risco de contraste mesmo em pesos leves.
- Estado loading:
aria-busy="true"no container. Os blocos skeleton não têm texto — o leitor de tela não anuncia os placeholders. Dimensões preservadas para evitar layout shift. - Estado erro:
role="alert"no.peppe-card__error-blockpara anúncio imediato pelo leitor de tela. - Estado vazio: texto propositivo lido naturalmente. Sem
role="alert"— estado vazio não é urgente. - Chip delta: o
.peppe-chipno slot delta recebearia-labelexplícito quando o texto não é autoexplicativo (ex.:aria-label="+R$ 230 vs. média mensal de delivery"). - Fallback de canal — voz: SDUI entrega kicker + value + horizon + caption. Delta é omitido (o sinal de cor não tem equivalente auditivo). Horizon é lido como "em quinze de dezembro".
Do / Don't
Do
R$ 1.200
em 15/dez
Value em display serif — carrega voz. O usuário lê como "mensagem do Peppe", não como tabela de dados.
Don't
R$ 1.200
em 15/dez
Value em sans bold cinza — parece tabela de extrato, perde a voz. A hierarquia tipográfica que diferencia o Balance Card de uma linha de listagem desaparece.
Do
Chip com tone coerente — success para positivo, alert para negativo. A cor é sinal funcional: o usuário entende o estado antes de ler o número.
Don't
Delta com cor inventada fora das funcionais — azul, roxo, laranja de marca. A cor decorativa dilui a hierarquia: quando tudo é colorido, nada é sinal. Anti-padrão visual #5.
Do
Caption curta e útil — amarra o contexto que o value não entrega: "após viagem e compromissos fixos". O usuário entende por que o saldo é esse número. Uma informação por caption.
Don't
Caption decorativa sem dado: "seu saldo tá aqui!" ou "veja abaixo os detalhes". Não amarra, não informa — é filler. Anti-padrão de conteúdo #1 — Filler de abertura e #8 — Pleasantries.
Componentes relacionados
- Card — scaffold do Balance Card. O Balance Card é instância nomeada de Card; herda superfície raised, sombra, border gradient e raio.
- Chip — usado no slot
deltacom tonesuccess(positivo) oualert(negativo). Nunca criar chip com cor fora das funcionais. - Section Hero — quando o Balance Card é o hero da tela, o Section Hero pode preceder com eyebrow + title de saudação contextual. O Section Hero não contém o Balance Card — os dois vivem em sequência no container pai.
- Commitment Card — composition irmã. Segue o Balance Card na hierarquia do dashboard — o saldo projetado (aqui) contextualiza os compromissos (lá).
Markup de referência
HTML namespaced copiável. Slots opcionais podem ser omitidos sem quebrar o componente — apenas o kicker e o value são obrigatórios.
Decisão de arquitetura: o Balance Card usa composição de classes — peppe-card peppe-balance-card. A classe peppe-card entrega a superfície raised (gradiente, border gradient, sombra, overflow hidden); a classe peppe-balance-card entrega o layout interno dos slots e os papéis tipográficos específicos. Nenhuma duplicação de CSS de superfície.
<!-- Balance Card — variante completa (todos os slots) --> <article className="peppe-card peppe-balance-card" aria-label="saldo projetado em 15/dez: R$ 1.200. +R$ 230 vs. média. Após viagem e compromissos fixos."> <!-- kicker: obrigatório — SALDO PROJETADO ou SALDO ATUAL --> <span className="peppe-balance-card__kicker">SALDO PROJETADO</span> <!-- value: obrigatório — BRL locale PT-BR, serif display --> <p className="peppe-balance-card__value">R$ 1.200</p> <!-- horizon: opcional mas recomendado — âncora temporal --> <p className="peppe-balance-card__horizon">em 15/dez</p> <!-- delta: opcional — Chip com tone success (positivo) ou alert (negativo) --> <div className="peppe-balance-card__delta"> <span className="peppe-chip peppe-chip--success" aria-label="+R$ 230 vs. média">+R$ 230 vs. média</span> </div> <!-- caption: opcional — Peppe amarrando o contexto (1 linha máx.) --> <p className="peppe-balance-card__caption">após viagem e compromissos fixos</p> </article> <!-- Balance Card — estado loading (skeleton) --> <article className="peppe-card peppe-balance-card peppe-balance-card--loading" aria-busy="true" aria-label="Carregando saldo"> <div className="peppe-card__skeleton-line peppe-card__skeleton-line--xs peppe-balance-card__skeleton-kicker"></div> <div className="peppe-balance-card__skeleton-value"></div> <div className="peppe-card__skeleton-line peppe-card__skeleton-line--sm peppe-balance-card__skeleton-horizon"></div> <div className="peppe-card__skeleton-line peppe-card__skeleton-line--md peppe-balance-card__skeleton-caption"></div> </article> <!-- Balance Card — estado erro --> <article className="peppe-card peppe-balance-card peppe-card--error" aria-label="Erro ao carregar saldo"> <div className="peppe-card__error-block" role="alert"> <svg className="peppe-card__error-icon" ...></svg> <p className="peppe-card__error-text"> Não consegui carregar o saldo agora. Tenta de novo? </p> <button className="peppe-button peppe-button--tertiary" type="button"> <span className="peppe-button__label">Tentar de novo</span> </button> </div> </article> <!-- Balance Card — estado vazio --> <article className="peppe-card peppe-balance-card peppe-card--empty" aria-label="Sem histórico de saldo"> <div className="peppe-card__empty-block"> <svg className="peppe-card__empty-icon" ...></svg> <p className="peppe-card__empty-text"> Sem histórico ainda. Conecta sua conta pra eu acompanhar. </p> </div> </article>