Pular para o conteúdo

Referência interna da equipe

Design system

Só equipe

Pop-art tabloide: papel bege, tinta quase-preta, borda de 2px, sombra sólida deslocada (nunca blur) e título condensado em caixa alta. Esta página é a fonte da verdade — se uma tela do site não se parece com o que está aqui, a tela está errada.

01

Paleta

As classes utilitárias são geradas pelo @theme do Tailwind v4 em app/globals.css. Use as classes (bg-, text-, border-) no JSX; as variáveis CSS só dentro do próprio globals.css.

Esta lista é lida de app/globals.css na hora do build: os tokens vêm do :root, o nome da classe vem do @theme inline e cada amostra é pintada com var(--token). Não há hex digitado nesta página — mudou o CSS, mudou aqui.
Cada família tem dois ofícios: a cor clara PREENCHE, a escura ESCREVE. Nas coloridas, 100 e 500 preenchem (fundo de pílula, botão, ponto de legenda) e 600/700 escrevem (texto, ícone, número). Texto pintado com o 500 sobre o papel bege reprova em WCAG AA — o ouro escrevia a 2,09:1 na data da linha do tempo e na pílula “Mandatos”. A tinta é a exceção, porque ela é toda escrita: tinta-soft é o degrau mais claro que ainda dá para ler, e ele carrega o crédito CC BY obrigatório do acervo e o peso do arquivo em /downloads — estava em 3,06:1. Os dois foram corrigidos no token, não classe por classe. O piso medido sobre este papel é #75695B: mais claro que isso não escreve.

Token marcado como sem classe é de propósito, não esquecimento: tinta-700, vermelho-300, vermelho-700 e ouro-700 só são consumidos dentro do próprio app/globals.css ou numa custom property em style (a cor de item do Filtro) — lugares onde uma classe utilitária não serviria para nada. Token que não é usado em lugar nenhum não fica no meio-termo: sai da lista. Cinco saíram nesta rodada (ouro-300, verde-300, verde-500, ceu-300, ceu-500).

Papel (fundos)

  • papel-bright--papel-100 · #FCF6E8
  • papel--papel-200 · #F7ECD4
  • papel-inset--papel-300 · #EFE0C2
  • papel-deep--papel-400 · #E3CFA6

Tinta (texto e bordas)

  • tinta-soft--tinta-400 · #75695B
  • tinta-muted--tinta-500 · #5A4D41
  • sem classe--tinta-700 · #2B231C
  • tinta--tinta-800 · #1C1611
  • tinta-strong--tinta-900 · #140E0B

Vermelho (destaque, militância)

  • vermelho-soft--vermelho-100 · #FBDBD7
  • sem classe--vermelho-300 · #F08D86
  • vermelho-bright--vermelho-500 · #E03A2E
  • vermelho--vermelho-600 · #C62B25
  • sem classe--vermelho-700 · #9E211C

Ouro (aviso, em breve)

  • ouro-soft--ouro-100 · #FCEFC8
  • ouro--ouro-500 · #F4BE45
  • ouro-ink--ouro-600 · #8A5D14
  • sem classe--ouro-700 · #6B4710

Verde (ok, publicado)

  • verde-soft--verde-100 · #D5EFDF
  • verde--verde-600 · #0E7544

Céu (informação neutra)

  • ceu-soft--ceu-100 · #DCE9F3
  • ceu--ceu-600 · #2C5C8A

Tokens que não são cor

  • --pop-sm: 3px 3px 0 0 var(--tinta-800)
  • --pop-md: 5px 5px 0 0 var(--tinta-800)
  • --pop-red: 5px 5px 0 0 var(--vermelho-600)
  • --raio: 14px
  • --raio-sm: 8px
02

Tipografia

Três famílias, três funções. Nunca use Bungee em bloco de texto e nunca use Archivo em título de destaque.

Anton — .bnv-impact / font-impactTítulos

Queremos Benedita senadora

Condensada, caixa alta, manchete. É o título padrão de página e de cartão.

Bungee — .bnv-display / font-displaySelo / número

SERÁ?!

Peso de cartaz. Use só em pedaço curto: selo, número, interjeição. Nunca em frase longa.

Archivo — font-body (padrão do body)Corpo

Texto corrido, descrição de cartão, legenda e tabela. É o que o body já usa por padrão — não precisa declarar.

.bnv-kicker — linha de apoio em caixa alta

.bnv-prosa — 15px / 1.625Leitura

Ementa de projeto de lei, fala de roteiro, markdown de guia. É o texto que a equipe LÊ por quarenta minutos, não o que ela bate o olho: um degrau acima do metadado, com a entrelinha folgada de parágrafo.

Prosa é 15px; metadado continua text-sm (14px). Rótulo, legenda, número e linha de tabela NÃO são prosa — não ponha esta classe neles. Os números não são novos: era text-[15px] copiado à mão nove vezes, em seis arquivos — oito delas com leading-relaxed ao lado, e uma sem —, e 1.625 é o valor exato do leading-relaxed do Tailwind, então a classe tem delta ZERO de pixel para as oito. Migrou components/md.tsx em 24/08/2026 e, em 26/08/2026, /pautas, /producao, /projetos e /timeline. Sobra UMA à mão, em /roteiros: lá o text-[15px] vem SEM leading-relaxed, então a troca GANHA entrelinha e muda a altura do texto na tela — decisão de olho, não troca de nome.

03

Pagina

Casca de TODA página do site: container (max-w-5xl), cabeçalho e régua. É uma largura só — se uma tela declara o próprio <main> ou o próprio <header>, a tela está errada. Página que precisa de coluna estreita para ler (a linha do tempo) limita a largura do CONTEÚDO por dentro, nunca recriando outro container por fora. Esta própria página é uma instância — o cabeçalho lá em cima é o componente.

<Pagina
  kicker="Referência interna"   // opcional
  titulo="Design system"
  subtitulo="Parágrafo de apoio" // opcional
  acao={<Etiqueta>Só equipe</Etiqueta>} // opcional, à direita
>
  {conteúdo}
</Pagina>

Renderiza <main> — não aninhe uma Pagina dentro de outra.

04

Cartao

Com href o cartão inteiro vira link e ganha o deslocamento no hover. Passe o mouse para ver.

Cartão estático

Sem href: é só uma caixa. Não desloca no hover, não tem foco.

Cartão com link

Publicado

Com href interno: vira <Link> do Next, sobe 2px no hover e engrossa a sombra.

Cartão em destaque

Prioridade

destaque: borda e sombra vermelhas. Use no máximo um por tela — se tudo é destaque, nada é.

Cartão externo

Instagram

URL http(s): abre em nova aba com rel=noreferrer e mostra o indicador de link externo.

Abre em outra aba

Com meta e children

Em brevev2

meta vai no canto superior direito; children entra abaixo da descrição.

Formato:
Reels
Cenas:
6

Destaque + link

As duas variantes combinam: sombra vermelha que cresce no hover.

05

Botao

A primitiva de AÇÃO. Todo botão, link-de-ação e pílula de navegação do site sai daqui. Se você está escrevendo border-2 border-tinta px-4 py-1.5 num <button>, pare: é este componente. Passe o mouse — é o mesmo deslocamento do Cartao.

Tons

  • vermelho — a ação principal da tela. Uma por tela: salvar, criar, baixar tudo.
  • ouro — acréscimo. É o + Alguma coisa do cabeçalho.
  • papel — ação secundária de igual peso: cancelar, copiar, fechar (padrão).
  • fantasma — ação de lista ou navegação. Sem peso em repouso, ganha borda e sombra quando o mouse encosta. É o tom da barra de cima e das listas laterais, onde dez botões sólidos viram ruído.

Tamanhos

sm é para ação secundária DENTRO de um cartão (“editar”, “copiar”): sombra de 2px em vez de 3px, para não competir com o cartão que a contém. Não existe tamanho “xs” — se não coube, o botão não devia estar ali.

Botão que é link

Com href vira <Link> do Next em rota interna e <a> em URL externa (nova aba, rel=noreferrer) ou com download — asset estático não passa pelo router. Nos dois casos continua focável por teclado, com o mesmo foco vermelho do Cartao.

Cor e tamanho vêm do prop, nunca do className. .bnv-botao mora fora de @layer e, no cascade do Tailwind v4, ganha do utilitário: um bg-ouro ou text-xs no className seria simplesmente ignorado. O className fica para posição e largura (absolute, w-full, ml-auto).
06

Campo

A primitiva de ENTRADA: input, textarea e select. Existiam três versões à mão no site, uma delas com borda de 1px — justamente no formulário que a equipe usa todo dia.

  • rotulo põe o texto em .bnv-kicker e embrulha o campo num <label> — sem htmlFor, sem id inventado e sem rótulo órfão.
  • Largura é sempre 100% do container. Para um campo estreito, aperte o container (<div className="w-24">) — um w-24 no className seria ignorado, pela mesma regra de cascade do Botao.
  • Altura de textarea (min-h-20) e margem (mb-5) passam normalmente pelo className: .bnv-campo não define nenhuma das duas.
  • 16px de fonte, e o número é de celular, não de gosto: o Safari do iPhone dá zoom sozinho em campo com fonte menor que 16px. Eram 14px, e o layout saltava a cada toque para escrever.
  • 44px de altura mínima em input e select — o mesmo piso de toque do .bnv-botao. Medidos em 375×812, os input davam 36px e os select, 32px, e select de 32px no dedo erra muito. O textarea fica de fora: ele cresce por rows e min-h-*, e um piso ali brigaria com o className — foi assim que a caixa de “copie com Ctrl+C” do briefing encolheu de 160px para 63px, calada, na primeira tentativa desta regra.
  • Foco: a borda vira vermelha. Nunca tire isso — é o único sinal de foco.
  • required escreve obrigatório no rótulo (por extenso, não um asterisco) e min/max limitam o campo numérico. Fora da faixa, a borda acende em :user-invalid — depois que a pessoa mexeu, nunca no formulário em branco. É aviso, não trava: quem barra de verdade é a server action, que relê a mesma faixa. Sai do schema de lib/editavel.ts, então não se redigita campo por campo.
07

Etiqueta

Pílula curta em Anton caixa alta. Uma a três palavras, nunca frase. O tom carrega significado — mantenha o mesmo tom para o mesmo tipo de informação em todo o site.

NeutroVermelhoOuroVerdeCéu
  • neutro — categoria, contagem, rótulo sem carga (padrão).
  • vermelho — urgência, prioridade, restrito à equipe.
  • ouro — em breve, rascunho, atenção.
  • verde — pronto, aprovado, publicado.
  • ceu — informação neutra, canal, fonte externa.
Selo com pop

pop acrescenta a sombra sólida de 2px. Só quando a etiqueta é um selo sobre outra superfície (o “Equipe” da barra de cima). Dentro de cartão ela é metadado e não deve competir com o cartão.

EtiquetaLink — a etiqueta que se clica

  • A Etiqueta é <span> e não se clica. Atalho para uma seção, download de arquivo e link para outra tela usam esta.
  • Ela já nasce com o .bnv-lift-sm certo (a pílula não tem sombra em repouso; um .bnv-lift nela saltava de 0 para 5px no hover) e com altura de alvo de toque. Existia copiada como <a className="bnv-tag bnv-lift"> em seis páginas, e o copiar-colar levava os dois defeitos junto.
  • Rota interna vira <Link>; #âncora e download viram <a> (o router não roteiriza um .jpg); URL externa abre em outra aba. Para o arquivo em public/ que é interno mas não é rota, use novaAba.
08

Vazio

Estado sem conteúdo. Borda tracejada e sem sombra — é o oposto visual do cartão, para não parecer clicável.

Nada por aqui ainda

Versão mínima: só título e texto.

Nenhum roteiro nesta linha

Assim que o primeiro roteiro for aprovado, ele aparece aqui automaticamente.

AguardandoVault
09

Aviso

Faixa de alerta em linha, sem sombra — de propósito: não é cartão e não é clicável. Vem logo depois do bloco que ela qualifica (já traz o espaço acima). Uma frase, no máximo duas.

Um cartão qualquer, com o aviso colado embaixo dele:

vermelho (padrão) — “não erre isto”. Regra dura, risco jurídico, cânone que nenhum roteiro pode contrariar. Exemplo real: os ETs nunca chegam na Terra.
ouro — “atenção”. Pendência, ressalva, arquivo desatualizado, informação que muda a leitura mas não proíbe nada.

É uma div, então aceita bloco (parágrafo, lista, MD) — não só texto. Se o alerta virar três parágrafos, ele não é aviso: é um cartão em destaque.

10

Caminho

Caminho de arquivo do vault. Quebra no meio da palavra, porque caminho de vault estoura qualquer cartão.

Nota no vault: Beneverso_Vault/11_🎭_Personagens/02_ETs_no_Rio/Lore_ETs_no_Rio.md

Pasta: _Assets_Producao/Fotos Bené/

Só para caminho de arquivo ou pasta. Para nome de classe, de token ou de propriedade, use Cod — é o que esta página usa em todo o resto. Os dois vestem a mesma pele, a classe .bnv-cod; muda só o corpo da fonte.

É a única borda de 1px da casa, e é exceção declarada — não conserte. Aqui a borda não delimita uma caixa: marca um pedaço de texto no meio da frase. Dois pixels em volta de 11px viram tarja preta e empurram a entrelinha do parágrafo inteiro. O canto, esse não tinha desculpa — era o rounded do Tailwind, um quinto canto inventado, e agora é --raio-sm como qualquer controle.
11

Classes utilitárias

Definidas em app/globals.css. Use-as antes de inventar estilo novo.

ClassePara quê
.bnv-impactAnton — título de página e de cartão.
.bnv-displayBungee — selo, número, interjeição curta.
.bnv-kickerLinha de apoio em caixa alta, espaçada.
.bnv-prosaO corpo de LEITURA: 15px com entrelinha 1.625. Ementa, fala de roteiro, markdown de guia. Metadado continua text-sm — ver a seção 02. Sobra UMA text-[15px] escrita à mão, em /roteiros — a única sem leading-relaxed, e por isso a única cuja troca mudaria a altura do texto na tela.
.bnv-cardCaixa: papel claro, borda 2px, sombra 3px.
.bnv-card-destaqueVariante do cartão com borda e sombra vermelhas.
.bnv-pop / .bnv-pop-redSombra sólida deslocada de 5px (preta / vermelha).
.bnv-liftHover que desloca o bloco e engrossa a sombra + foco visível.
.bnv-lift-smO mesmo para peça miúda (2px→4px), e com sombra JÁ em repouso: um .bnv-lift numa .bnv-tag saltava de 0 para 5px.
.bnv-botao (+ -vermelho/-ouro/-papel/-fantasma/-sm)Base do Botao e seus tons. Use o componente, não a classe.
.bnv-campo / .bnv-checkBase do Campo e da caixa de marcar.
.bnv-tag (+ -vermelho/-ouro/-verde/-ceu/-pop)Base da Etiqueta e seus tons. Tem teto de largura: pílula com texto livre de formulário corta com reticências em vez de empurrar o cartão para fora da tela.
a.bnv-tag / button.bnv-tagEtiqueta clicável ganha 32px de altura — alvo de toque (WCAG 2.2). A regra pega pelo elemento, então vale também para quem escreve bnv-tag na mão; o <span> estático não muda.
.bnv-codMarca de código inline (Caminho e Cod). Uma das TRÊS bordas de 1px declaradas da casa — exceção escrita, ver a seção 12.
.bnv-reguaBarra grossa que fecha o cabeçalho da página.
.bnv-fileteA régua FINA que separa blocos dentro de um cartão: rodapé de metadados, linha de fontes, nota embaixo de uma amostra. A receita de 1px a 10% (border-t border-tinta/10) acabou — zero ocorrências, e a seção 35 do npm test reprova a primeira que voltar. A de 2px a 15% continua em SEIS lugares, de propósito: ver a seção 12.
.bnv-filete-bA MESMA régua, na aresta de baixo. Existe por um caso só e real: em /downloads cada <li> da lista de arquivos leva o filete, e o último ficaria aberto — não há um <li> seguinte para carregar a régua de fechamento. Mesmo token, outra aresta: nenhum valor novo foi escolhido.
.bnv-trilhoA barra lateral VERTICAL de bloco recuado: citação, nota de apoio, contagem conferida. Carrega só a geometria — 2px e 0.625rem de recuo, que eram a maioria das SETE receitas escritas à mão. A cor fica de fora de propósito, porque ouro, vermelho e tinta significam coisas diferentes: venha sempre com uma utilitária (border-ouro, border-tinta/20), ou a borda cai em currentColor. Migrou /biblioteca, com delta zero de pixel; as outras mudam o recuo e isso é decisão de olho.com border-ourocom border-tinta/20
.bnv-vazioCaixa tracejada do estado vazio.
12

Regras da casa

Pode

  • Borda sólida de 2px em tinta.
  • Sombra deslocada sólida, sem blur.
  • Título condensado em caixa alta.
  • Vermelho como acento, não como fundo de página.
  • Papel bege sempre por baixo.

Não pode

  • Cinza corporativo (slate, zinc, gray do Tailwind).
  • Gradiente suave e sombra difusa.
  • Borda de 1px em cartão, botão ou campo.
  • Bungee em parágrafo.
  • rounded-md/lg/xl solto — os cantos são dois: rounded-bnv (caixa) e rounded-bnv-sm (controle).
  • Botão remontado à mão em vez do Botao — foi assim que a cara se perdeu.
  • Cor fora da paleta — se faltar, fale com o F0.
  • Texto pintado com o degrau 500 de uma cor sobre papel: reprova em contraste. A clara preenche, a escura escreve.
  • Controle clicável abaixo de 24px de altura — no celular o dedo não acerta. O alvo cheio é 44px, e o Botao já garante esse piso sozinho (min-height na .bnv-botao): não escreva min-h-11 na mão. O tamanho="sm" fica em 28px de propósito, porque são vários numa fileira dentro de cartão.
  • Régua fina escrita à mão (border-t border-tinta/10 ou border-b). Existe a .bnv-filete — e a .bnv-filete-b, para a aresta de baixo. A seção 35 do npm test reprova a primeira que voltar.

Exceções declaradas

Estas contrariam a lista de cima de propósito, com o motivo escrito. Estão aqui para ninguém “consertar” depois e o desvio voltar por acidente.

  • Borda de 1px em .bnv-cod (Caminho e Cod). Ali a borda não fecha uma caixa, marca um pedaço de texto dentro da frase: 2px em volta de 11px vira tarja e desloca a entrelinha do parágrafo. Vale só para código inline — cartão, botão e campo continuam com 2px, sem discussão.
  • Borda de 1px na .bnv-filete e na .bnv-filete-b — a régua fina dentro de um cartão, nas duas arestas. Também não fecha caixa: o cartão já tem a moldura de 2px em volta, e uma segunda de 2px por dentro faz o bloco parecer rachado ao meio. Esta frase já esteve falsa duas vezes: o globals.css dizia que .bnv-cod era a ÚNICA borda de 1px da casa, depois que eram DUAS, enquanto border-t border-tinta/10 continuava escrito à mão pelo app. Em 20/08/2026 a receita de 1px a 10% acabou de verdade — zero ocorrências fora do globals.css, e a seção 35 do npm test reprova o retorno da primeira. O que ainda não acabou está dito aqui em vez de escondido: seguem SEIS border-t-2 border-tinta/15 escritas à mão, em /ideias, /kanban e /roteiros. Trocá-las por esta classe muda 2px por 1px na tela, e isso é decisão de olho — quem fez esta rodada trabalhou sem navegador. A seção 35 trava o número em seis: a sétima reprova.
  • style inline no Filtro, pela custom property --filtro-cor: o JIT do Tailwind não enxerga classe montada em runtime. Detalhe na seção 13.
13

Filtro

A primitiva de SELEÇÃO: um grupo de pílulas onde exatamente uma está sempre marcada. É o que a linha do tempo e a biblioteca usavam à mão — uma com <button> e style inline, a outra com uma função Botao local — até virarem o mesmo componente. Se você está escrevendo bnv-tag num <button> com aria-pressed, pare: é este componente.

Neutro — sem cor de tema

Categoria

O padrão. Pílula vazada com borda de tinta em repouso; marcada, ela vira preenchimento sólido com texto claro. O primeiro item é sempre o “tudo” do grupo — filtro sem escape não é filtro, é armadilha.

Cor por item

Tema

Quando a categoria tem cor (os temas da linha do tempo), passe cor no item — qualquer cor CSS, inclusive var(--ceu-600). Ela vira a borda em repouso, o preenchimento quando marcada, e acende o pontinho marcador. Item sem cor não ganha ponto: o ponto é legenda de cor, não sinal de estado.

Sem rótulo visível

rotulo é obrigatório e vira o kicker acima do grupo. Com ocultarRotulo ele some da tela mas continua sendo o aria-label do grupo — nunca existe grupo anônimo.

Como se escreve

<Filtro
  rotulo="Tema"            // obrigatório: kicker + aria-label do grupo
  ocultarRotulo            // opcional: só o aria-label
  className="mb-6"         // opcional: posição e largura do bloco
  valor={solo}             // o item marcado agora
  onChange={(v) => setSolo(v)}
  itens={[
    { valor: null, rotulo: "Todos os temas" },
    { valor: "leis", rotulo: "Leis & conquistas", cor: "var(--verde-600)" },
  ]}
/>
  • valor pode ser null — é como se escreve o item “todos” sem inventar uma string sentinela.
  • onChange dispara com o valor clicado mesmo se ele já estiver marcado. Quem quiser “clicar de novo para desmarcar” resolve no próprio handler ((v) => setSolo(v === solo ? null : v)) — o componente não adivinha.
  • É role="radiogroup" com aria-checked, e não um punhado de aria-pressed: a seleção é única e sempre há um marcado, então o leitor de tela tem que ouvir “1 de 6”, não seis interruptores soltos. Vem junto o que o papel de rádio exige — o grupo é uma parada de tabulação e as setas (mais Home/End) andam por ele.
  • O estado marcado não depende de matiz: ele inverte a luminância (sólido + texto claro contra vazado + texto escuro). Continua legível em escala de cinza.
A cor vem do item, nunca de uma classe montada em runtime. Um bg-{cor} jamais existiria: o JIT do Tailwind não enxerga string concatenada em runtime, e mesmo que enxergasse, .bnv-tag mora fora de @layer e ganharia do utilitário. O Filtro resolve com a custom property --filtro-cor no style do próprio elemento — é a exceção legítima à regra de não usar style inline.
14

CabecalhoSecao

A divisão de seção DENTRO de uma página: régua de 2px, marcador opcional à esquerda e um lugar fixo à direita para a contagem. Estava montada à mão em doze telas e já tinha divergido — metade escrevia flex, metade flex flex-wrap, e o h2 alternava entre text-xl e text-lg. É a mesma história da EtiquetaLink, com número maior.

Só o título

De onde isto vem

Com marcador e contagem

Fora do acervo — não publicar

7

Roteiros prontos

12 peças

Como se escreve

<section className="mt-10">
  <CabecalhoSecao
    marcador="✕"                   // número, ✕ ou letra — sai em Bungee vermelho
    emoji="🎬"                     // marcador que é emoji: maior, aria-hidden
    titulo="Fora do acervo"
    grande                         // text-2xl em vez de text-xl
    direita={<Etiqueta>12</Etiqueta>}
  />
  …
</section>
  • Ele não envolve a <section>: cada tela decide id e espaçamento de topo — e esses variam de verdade entre elas. O scroll-mt saiu desta lista em 24/08/2026: a folga da barra grudenta virou html { scroll-padding-top: 6rem } e vale para toda âncora do site. Não reescreva a classe na tela — scroll-padding e scroll-margin SOMAM.
  • flex-wrap é padrão e não é opção: sem ele a contagem empurra o título para fora da tela no celular.
  • direita quase sempre é uma Etiqueta com a contagem — e a contagem é derivada, nunca digitada.
15

SecaoMd

Um trecho rotulado de texto longo: kicker em caixa alta e markdown embaixo. É o par da Etiqueta para PROSA — a etiqueta rotula um dado curto, esta rotula um parágrafo. Estava escrita duas vezes, byte a byte igual, em /pautas e /adversarios: as duas telas onde a equipe lê texto longo.

Como o Beneverso pode mencionar

Sempre com fonte. Sem fonte registrada, a menção não sai.

  • Fato verificado, com link
  • Sem adjetivo que a fonte não sustenta
  • Texto vazio ou null não renderiza nada — nem o rótulo. Campo em branco no Supabase simplesmente não aparece, em vez de deixar um kicker órfão na coluna de detalhe. Há um SecaoMd logo acima desta lista com texto nulo: ele não está na tela, e é esse o comportamento.
  • Aceita markdown completo, pelo MD — negrito, lista, tabela e link.
16

ErroDeBanco

A tela de “o banco não respondeu”. Página inteira, não caixinha: quando o Supabase falha não sobra conteúdo nenhum para mostrar em volta. Existia copiada byte a byte em SETE page.tsx, com a mesma frase e só o título mudando — melhorar a mensagem exigia lembrar de sete arquivos.

A tela inteira, ao vivo

Falha de carregamento

Erro ao carregar as pautas

O banco não respondeu. Não é a sua conexão nem erro seu: o conteúdo desta tela vive no Supabase e ele não devolveu resposta. Recarregue; se insistir, copie a mensagem abaixo e mande para quem cuida do site.

relation "public.pautas" does not exist
const { data, error } = await supabase.from("pautas").select("*");

if (error) {
  return <ErroDeBanco assunto="as pautas" mensagem={error.message} />;
}
  • assunto é o complemento do título, na voz do site: “a linha do tempo”, “as pautas”, “produção”. O título sai como “Erro ao carregar …”.
  • A mensagem crua do Postgres continua na tela de propósito — é o que a equipe copia e cola para pedir socorro —, mas agora abaixo de uma explicação em português e de um botão de saída.
  • Sete telas usam: /timeline, /pautas, /citacoes, /calendario, /adversarios, /producao e /projetos.
17

Placar

Fileira de números grandes: o estado do projeto numa olhada. Bungee no número, kicker no rótulo, caixa de borda 2px na cor do significado. Nasceu do placar da /biblioteca, que estava montado à mão lá dentro, e virou a abertura do painel da equipe.

Três colunas, sem link

61

Dentro do site

14

Só uma fatia

31

Só em pasta

Todo número de um placar é DERIVADO. Nenhum é digitado, e nenhum pode ser: número escrito à mão num painel envelhece calado, e ninguém percebe que passou a mentir. Os do painel da equipe saem de lib/kanban.ts, contagemNoSite() e lib/ideias.ts. Os desta seção são amostra de styleguide — a única situação em que valor literal é aceitável.
18

LinhaDl

Uma linha de FICHA: o par dt/dd do rodapé de metadados de um cartão — “Fonte”, “Checagem”, “Arquivo”, “No disco”. Estava declarada DUAS vezes, byte a byte igual, em app/acervo/page.tsx e app/midia/page.tsx: as duas telas de ficha do site. As nove linhas eram idênticas; só mudava o nome da função seguinte no arquivo.

Dentro do <dl> que fecha o cartão

Fonte
Agência Senado · Flickr, 2018
Checagem
Crédito conferido no metadado do arquivo, não deduzido do nome.
Arquivo
public/acervo/alta/bene-constituinte.jpg · 2400×1600 px

Como se escreve

<dl className="bnv-filete mt-3 space-y-1.5 pt-3">
  <LinhaDl rotulo="No disco">
    <Caminho>{item.caminho}</Caminho>
  </LinhaDl>
</dl>
  • Ela não traz o <dl>: quem decide o filete, o espaçamento e o [overflow-wrap:anywhere] é o cartão em volta. Mesmo princípio da CabecalhoSecao, que não envolve a <section>.
  • O rótulo não quebra (shrink-0) e o valor quebra em qualquer lugar (break-words + min-w-0) — é isso que impede um caminho de disco longo de estourar a largura do cartão.
  • O pt-[1px] do rótulo é deliberado: o kicker tem 10px e o valor 12px, e sem o pixel a caixa alta flutua acima da linha de base do texto ao lado.
19

BlocoDestaque

A caixa de “leia isto primeiro” que abre uma página: cartão de borda e sombra vermelhas, título e explicação, antes de qualquer lista. Estava montada à mão em NOVE lugares e já divergia em duas coisas ao mesmo tempo — o elemento (section em cinco, div em quatro) e a margem de topo (p-4 em seis, mt-6 p-4 em três).

Abrindo a página — sem margem de topo

Como usar sem errar

Copie a linha de crédito exatamente como está no cartão e cole na legenda do post. O aviso em vermelho existe porque ali dá pra errar feio.

Depois de uma fileira de etiquetas — com className

44 ideias9 já viraram peça

Ideia não é roteiro

Ideia com pergunta em aberto continua sendo ideia útil. O que ela não pode é parecer pronta.

Como se escreve

<BlocoDestaque titulo="Onde o trabalho está">   // abre a página
<BlocoDestaque className="mt-6" titulo="…">      // vem depois de algo
  • p-4 e bnv-card-destaque são congelados — eram 9 de 9, byte a byte. O className serve para a margem externa: ela é da página, não da caixa.
  • O título sai sempre como <h2> em text-xl — era 8 de 9.
  • O nono caso, o “Cânone — não contrarie” de /universo, ficou de fora de propósito: ali o título é <h3>/text-lg e a caixa vive dentro de uma grade de duas colunas. É caixa de destaque de COLUNA, não de abertura de página.
  • Não confunda com a CabecalhoSecao: aquela é a régua que separa blocos da PÁGINA; esta é um cartão. A regra de triagem entre as duas está escrita em components/bnv/cabecalho-secao.tsx e virou máquina na seção 34 do npm test.
20

Barra

Barra segmentada de proporção: uma fileira fina onde cada fatia ocupa a sua porcentagem do total. Estava escrita à mão em dois arquivos com o wrapper idêntico caractere por caractere — e o miolo já tinha divergido, que é o que decidiu a extração.

Partição — as fatias fecham em 100%

É a forma da tira do /equipe: os três estados de noSite particionam o catálogo, então o trilho nunca aparece. Os números abaixo são de exemplo — os de verdade estão no painel, e saem de contagemNoSite(). Copiá-los para cá só trocaria um número congelado por outro, e obrigaria a reeditar esta página a cada item novo do catálogo.

Progresso — o trilho é o que falta

É a forma da barra de aprovação de /roteiros/<slug>: as duas fatias somam MENOS que o total de propósito, e o pedaço de bg-papel-inset que fica sem pintura é o pendente. Os números também são de exemplo — cada roteiro tem os seus, e eles vêm de contarAprovados().

Os cinco tons

Como se escreve

<Barra
  className="mt-2"                       // margem externa, e SÓ ela
  total={ITENS.length}                   // o denominador, declarado
  rotulo={/* a frase do aria-label */}
  fatias={ESTADOS.map((e) => ({
    chave: e,
    valor: catalogo[e],
    tom: SELO_SITE[e].tom,               // o tom vem de lib/rotulos.ts
  }))}
/>
  • O tom nunca é escrito à mão. Ele vem de lib/rotulos.tsSELO_SITE[e].tom no painel, ESTADO_APROVACAO.aprovado.tom no roteiro. A tradução tom → classe de fundo mora dentro da primitiva, literal, porque o JIT do Tailwind não enxerga bg-${tom} montado em runtime.
  • total é prop, e não fatias.reduce(…): derivar o total da soma encheria a barra do roteiro até 100% e apagaria justamente o dado que aquela tela mostra — o quanto ainda falta.
  • Ela é role="img", então rotulo é obrigatório: sem ele a barra é uma imagem muda. Cada tela escreve a sua frase porque as duas contam coisas diferentes.
  • A geometria (h-2, rounded-bnv-sm, overflow-hidden, o trilho) é congelada — era byte a byte igual nos dois usos. O className serve para a margem externa, mesma regra do BlocoDestaque.
  • Não foi a CONTAGEM de cópias que justificou a primitiva — não existe “regra da terceira cópia” nesta casa, e o MAPA.md diz isso com todas as letras. Foi a DIVERGÊNCIA: em 27/08/2026 o wrapper ainda era idêntico, mas o roteiro pintava bg-verde/bg-vermelho à mão enquanto ESTADO_APROVACAO já declarava esses dois tons ao lado. Uma das cópias tinha começado a mentir.
21

Propostas de aparência

Direções para a prioridade 4 do Felipe (bonito, com estética própria), medidas e renderizadas. Em cada uma, o VIGENTE fica à esquerda e o PROPOSTO à direita, na mesma tela. Nasceram três: a primeira foi retirada quando a medição não a sustentou, e o lugar dela guarda o porquê. Nenhuma está aplicada a nenhuma rota: toda classe deste bloco começa com .bnv-proposta- e só esta seção a usa — o portão recusa a primeira que escapar daqui.

Isto é proposta, não decisão. Identidade é do Felipe, e as direções abaixo podem ser recusadas juntas — recusar também é resposta. O que esta seção resolve é outra coisa: cinco rodadas descreveram a prioridade 4 com precisão e nenhuma mostrou uma tela. Nenhum token do :root foi trocado; o bloco novo em app/globals.css é aditivo e nasce descartável — escolhida uma direção, o valor sobe para o :root e o bloco é apagado.

Antes: duas lacunas que se mediam e não existem

  • As duas fontes de display seriam usadas uma vez cada. Não são. grep -rn "font-display" app/ components/ --include=*.tsx devolve 1 — mas a única ocorrência é esta página citando o nome da classe. A via de verdade é .bnv-display e .bnv-impact, e essas estão vivas em dezenas de lugares. Medir o utilitário do Tailwind em vez da classe da casa é medir a vitrine e chamar de site. Não há lacuna de tipografia.
  • O quinto canto estaria espalhado. Também não. Fora desta vitrine, rounded-full aparece 6 vezes, todas em círculos de 6 a 48px — o ponto da timeline (h-1.5), a legenda de cor do Filtro (h-2) e da /pautas (h-2.5), o marcador da timeline (h-6) e o círculo do Vazio (h-12). Isso é círculo, não canto de caixa, e a regra da seção 12 fala de caixa — ela cita rounded-md/lg/xl e não cita rounded-full. Zero violação real.

Direção A · Ênfase — retirada em 30/08/2026

Esta proposta caiu porque a medição não a sustentou. Ela fica escrita, e não apagada, porque proposta derrubada por medida é resultado — e some sem deixar rastro se ninguém anotar.

O que ela dizia. Que o :root declara três degraus de sombra — --pop-sm, --pop-md, --pop-red — e que as telas usavam um só; que numa tela com trinta cartões nenhum era o primeiro. A proposta era dar ao components/bnv/cartao.tsx um prop de ênfase.

Por que é falsa, e o erro foi de método. Contei .bnv-pop e .bnv-pop-red — que de fato têm 0 e 0 usos fora desta vitrine — e concluí que os degraus não chegavam à tela. Chegam, por outra via: --pop-red por .bnv-card-destaque (10 usos em 8 arquivos) e --pop-md por .bnv-lift:hover (6 usos). Medir a classe utilitária em vez da classe da casa é exatamente o erro que o bloco acima acusa sobre font-display — cometido duas seções depois de eu o apontar.

E o prop já existe. components/bnv/cartao.tsx tem destaque?: boolean desde antes desta rodada, e ele liga a .bnv-card-destaque— a sombra vermelha mais a borda vermelha, que é ponto por ponto o "grau máximo" que a proposta ia inventar. A primitiva que eu queria propor é a que a casa já usa, em 14 arquivos de app/.

A ênfase também não satura. As 19 rotas que usam destaque usam entre 1 e 9 vezes cada — na /equipe, 2 cartões de 21. Ênfase binária que é usada com parcimônia está funcionando; não há tela sofrendo por falta de um degrau do meio.

O que sobrou, e não é proposta de aparência. O degrau do meio --pop-md não chega a nenhuma tela em repouso — só no :hover —, e a classe .bnv-popque existe para ele não é usada em lugar nenhum. Isso é higiene de token, e a seção 01 já tem a regra da casa para esse caso ("token que não é usado em lugar nenhum sai da lista"). Fica registrado aqui, não vira proposta, e não foi consertado por mim: o :root está fora da fronteira desta seção.

Direção B · Acento de peça

A lacuna. A casa ensina uma paleta em /guias/design-system-imagens — vermelho #D7263D, amarelo #FFD93D, roxo #5C2A7E, verde #1B998B — e obedece outra em app/globals.css. Os hex em comum entre as duas são 0. A área da equipe não reconhece a paleta que ela mesma ensina para as peças.

O que muda. O papel e a tinta ficam. As cores do guia entram só como filete — 4px no flanco do bloco —, nunca como fundo nem como texto: nenhuma delas foi medida em contraste sobre este papel bege, e o :root é explícito sobre o que isso custou ao ouro. Filete não escreve, então não precisa passar em AA.

Vigente — tudo bege

Produção
Acervo
Guia

Proposto — a família se anuncia

Produção
Acervo
Guia

Onde apareceria: no filete do components/bnv/cabecalho-secao.tsx, que já é a régua que separa as seções. Custo: 18 arquivos de app/ grep -rl "components/bnv/cabecalho-secao" app | wc -l. Qual família recebe qual cor é escolha do Felipe; o pareamento acima é amostra, não proposta editorial.

Direção C · Manchete

A lacuna. O Pagina abre toda rota com chapéu, título e subtítulo, e o título é grande sem ser manchete. A casa se descreve, no alto desta página, como pop-art tabloide — e tabloide é justamente contraste de escala. Não há nenhum.

O que muda. O título vai para Anton, caixa alta, sobre tarja de tinta, com o chapéu virando pílula dentro da tarja. É a direção que mais muda a cara do site de uma vez — e a que mais facilmente vira repintura, por isso vem por último.

Vigente

Referência interna da equipe

Design system

Papel bege, tinta quase-preta, borda de 2px.

Proposto

Referência interna

Design system

Papel bege, tinta quase-preta, borda de 2px.

Onde apareceria: components/bnv/pagina.tsx — o topo de toda rota. Custo: 32 arquivos de app/ grep -rl "components/bnv/pagina" app | wc -l. Mais que o dobro da direção A, e sem meio-termo: aplicada, ela muda todas as rotas no mesmo commit.