Referência interna da equipe
Design system
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.
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.
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.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/ 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 · #FCF6E8papel--papel-200 · #F7ECD4papel-inset--papel-300 · #EFE0C2papel-deep--papel-400 · #E3CFA6
Tinta (texto e bordas)
tinta-soft--tinta-400 · #75695Btinta-muted--tinta-500 · #5A4D41- sem classe--tinta-700 · #2B231C
tinta--tinta-800 · #1C1611tinta-strong--tinta-900 · #140E0B
Vermelho (destaque, militância)
vermelho-soft--vermelho-100 · #FBDBD7- sem classe--vermelho-300 · #F08D86
vermelho-bright--vermelho-500 · #E03A2Evermelho--vermelho-600 · #C62B25- sem classe--vermelho-700 · #9E211C
Ouro (aviso, em breve)
ouro-soft--ouro-100 · #FCEFC8ouro--ouro-500 · #F4BE45ouro-ink--ouro-600 · #8A5D14- sem classe--ouro-700 · #6B4710
Verde (ok, publicado)
verde-soft--verde-100 · #D5EFDFverde--verde-600 · #0E7544
Céu (informação neutra)
ceu-soft--ceu-100 · #DCE9F3ceu--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
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.
Queremos Benedita senadora
Condensada, caixa alta, manchete. É o título padrão de página e de cartão.
SERÁ?!
Peso de cartaz. Use só em pedaço curto: selo, número, interjeição. Nunca em frase longa.
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
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.
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.
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
Com href interno: vira <Link> do Next, sobe 2px no hover e engrossa a sombra.
Cartão em destaque
destaque: borda e sombra vermelhas. Use no máximo um por tela — se tudo é destaque, nada é.
Cartão externo
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
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.
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 coisado 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.
.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).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.
rotulopõe o texto em.bnv-kickere embrulha o campo num<label>— semhtmlFor, 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">) — umw-24no 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-camponã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
inputeselect— o mesmo piso de toque do.bnv-botao. Medidos em 375×812, osinputdavam 36px e osselect, 32px, e select de 32px no dedo erra muito. Otextareafica de fora: ele cresce porrowsemin-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.
requiredescreve obrigatório no rótulo (por extenso, não um asterisco) emin/maxlimitam 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 delib/, então não se redigita campo por campo.editavel.ts
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.
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.
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-smcerto (a pílula não tem sombra em repouso; um.bnv-liftnela 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>;#âncoraedownloadviram<a>(o router não roteiriza um .jpg); URL externa abre em outra aba. Para o arquivo empublic/que é interno mas não é rota, usenovaAba.
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.
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:
É 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.
Caminho
Caminho de arquivo do vault. Quebra no meio da palavra, porque caminho de vault estoura qualquer cartão.
Nota no vault: Beneverso_Vault/
Pasta: _Assets_Producao/
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.
rounded do Tailwind, um quinto canto inventado, e agora é --raio-sm como qualquer controle.Classes utilitárias
Definidas em app/globals.css. Use-as antes de inventar estilo novo.
| Classe | Para quê |
|---|---|
.bnv-impact | Anton — título de página e de cartão. |
.bnv-display | Bungee — selo, número, interjeição curta. |
.bnv-kicker | Linha de apoio em caixa alta, espaçada. |
.bnv-prosa | O 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-card | Caixa: papel claro, borda 2px, sombra 3px. |
.bnv-card-destaque | Variante do cartão com borda e sombra vermelhas. |
.bnv-pop / .bnv-pop-red | Sombra sólida deslocada de 5px (preta / vermelha). |
.bnv-lift | Hover que desloca o bloco e engrossa a sombra + foco visível. |
.bnv-lift-sm | O 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-check | Base 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-tag | Etiqueta 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-cod | Marca 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-regua | Barra grossa que fecha o cabeçalho da página. |
.bnv-filete | A 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-b | A 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-trilho | A 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-vazio | Caixa tracejada do estado vazio. |
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/xlsolto — os cantos são dois:rounded-bnv(caixa) erounded-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
500de 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
Botaojá garante esse piso sozinho (min-heightna.bnv-botao): não escrevamin-h-11na mão. Otamanho="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/10ouborder-b). Existe a.bnv-filete— e a.bnv-filete-b, para a aresta de baixo. A seção 35 donpm testreprova 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-filetee 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: oglobals.cssdizia que.bnv-codera a ÚNICA borda de 1px da casa, depois que eram DUAS, enquantoborder-t border-tinta/10continuava escrito à mão pelo app. Em 20/08/2026 a receita de 1px a 10% acabou de verdade — zero ocorrências fora doglobals.css, e a seção 35 donpm testreprova o retorno da primeira. O que ainda não acabou está dito aqui em vez de escondido: seguem SEISborder-t-2 border-tinta/15escritas à mão, em/ideias,/kanbane/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. styleinline noFiltro, pela custom property--filtro-cor: o JIT do Tailwind não enxerga classe montada em runtime. Detalhe na seção 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)" },
]}
/>valorpode sernull— é como se escreve o item “todos” sem inventar uma string sentinela.onChangedispara 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"comaria-checked, e não um punhado dearia-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.
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.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
7Roteiros prontos
12 peçasComo 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 decideide espaçamento de topo — e esses variam de verdade entre elas. Oscroll-mtsaiu desta lista em 24/08/2026: a folga da barra grudenta virouhtml { 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.direitaquase sempre é umaEtiquetacom a contagem — e a contagem é derivada, nunca digitada.
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.
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
nullnã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á umSecaoMdlogo 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.
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,/producaoe/projetos.
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.
Quatro colunas, com link
Três colunas, sem link
61
Dentro do site
14
Só uma fatia
31
Só em pasta
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.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/· 2400×1600 pxacervo/ alta/ bene-constituinte.jpg
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 daCabecalhoSecao, 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.
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
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-4ebnv-card-destaquesão congelados — eram 9 de 9, byte a byte. OclassNameserve só para a margem externa: ela é da página, não da caixa.- O título sai sempre como
<h2>emtext-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-lge 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 emcomponents/bnv/cabecalho-secao.tsxe virou máquina na seção 34 donpm test.
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.ts—SELO_SITE[e].tomno painel,ESTADO_APROVACAO.aprovado.tomno roteiro. A tradução tom → classe de fundo mora dentro da primitiva, literal, porque o JIT do Tailwind não enxergabg-${tom}montado em runtime. totalé prop, e nãofatias.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ãorotuloé 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. OclassNameserve só para a margem externa, mesma regra doBlocoDestaque. - 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.mddiz isso com todas as letras. Foi a DIVERGÊNCIA: em 27/08/2026 o wrapper ainda era idêntico, mas o roteiro pintavabg-verde/bg-vermelhoà mão enquantoESTADO_APROVACAOjá declarava esses dois tons ao lado. Uma das cópias tinha começado a mentir.
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.
: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=*.tsxdevolve 1 — mas a única ocorrência é esta página citando o nome da classe. A via de verdade é.bnv-displaye.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-fullaparece 6 vezes, todas em círculos de 6 a 48px — o ponto da timeline (h-1.5), a legenda de cor doFiltro(h-2) e da/pautas(h-2.5), o marcador da timeline (h-6) e o círculo doVazio(h-12). Isso é círculo, não canto de caixa, e a regra da seção 12 fala de caixa — ela citarounded-md/lg/xle não citarounded-full. Zero violação real.
Direção A · Ênfase — retirada em 30/08/2026
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
Proposto — a família se anuncia
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
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.