Um hero sem foto. No lugar da mídia, um painel escuro recuado das bordas da página — canto arredondado, um fio de borda, duas faixas de luz muito fracas nos cantos — e dentro dele quatro coisas empilhadas no centro dos dois eixos: a linha de prova social com três rostos sobrepostos, a promessa, o apoio e um botão. Nada mais. O olho entra pelos rostos (são a única mancha de cor da dobra), sobe para a frase, desce pela explicação e para no botão, que é o último a aparecer e o único movimento diferente da dobra: os outros três tempos sobem, o botão estoura. Quem controla essa ordem não é o layout, é a coreografia — quatro tempos com atraso cravado, em que a headline vem palavra a palavra numa onda de 54ms.
Resolve a abertura de produto digital, onde não existe foto que ajude. Um hero institucional pode se apoiar em atmosfera; um hero de plataforma não tem o que fotografar — captura de tela envelhece, ilustração genérica não diz nada, e foto de banco mente sobre o que o produto é. Aqui o que convence é a frase, e a dobra é construída para isso: tudo o que não é a frase (o painel, as faixas, o grão) fica em 5% de opacidade, e a única coisa com peso visual além do texto é a prova social — que existe justamente porque uma promessa sozinha, sem ninguém por trás, é só uma afirmação.
Quando usar
- Primeira dobra de site de produto, plataforma ou SaaS, onde o argumento é a promessa e não a imagem. Foi o caso de origem.
- Abertura de assessoria ou consultoria, pelo mesmo motivo: o que se vende é critério, e não existe foto de critério. A dobra fica com a promessa em duas linhas, o patrimônio sob assessoria como prova social e um botão só, levando ao diagnóstico — é o que o markup desta pasta faz.
- Quando existe um número de prova social real e verificável, e um caminho adiante — um diagnóstico para agendar, uma conta para criar.
- Quando a página é escura e arredondada. O painel recuado com fio de borda é o que faz a home parecer um aplicativo; num projeto flat (
--radius-media:0) a dobra perde metade da razão de existir. - Promessa de 6 a 8 palavras e apoio de duas linhas. Acima disso o painel de 100vh não fecha o botão acima da linha d'água num notebook de 900 de altura.
Quando não usar
- Quando existe uma foto que carrega a dobra sozinha — imóvel, pessoa, lugar, atmosfera. É
hero/full-bleed-media, que sangra a mídia até a borda e ancora o texto embaixo para ela respirar. - Quando o produto precisa aparecer na primeira dobra: interface, trilho de telas, cards flutuando sobre a captura. É
hero/duas-colunas-produto, onde o palco é metade da dobra. - No fim da página, como última chamada antes do rodapé. A forma é parecida — texto centrado e um botão — mas o hero abre 100vh e come o encerramento; use
conversao/cta-fechamento-foto. - Quando a prova social são logos e não pessoas. Três rostos de 28px dizem "gente usa isto"; uma fileira de clientes diz outra coisa e pede
prova/marquee-logos. - Quando o número é o argumento, e não o rodapé dele. Um dado que merece corpo grande é
prova/stats-contador-regua, não um<strong>de 14px. - Quando a frase grande é um manifesto no meio da página, sem botão e sem prova. É
conteudo/manifesto-citacao.
Padrão editorial
| Slot | Regra |
|---|---|
| Rostos | Exatamente 3. Dois leem como casal; quatro passam de 100px de fila com a sobreposição de −8px e a linha quebra antes do texto. |
| Prova social — número | Um número redondo dentro do <strong class="pitch-social-n">. Quantidade verificável, nunca adjetivo: "R$ 2,4 bilhões sob assessoria", não "bilhões sob a guarda de famílias satisfeitas". |
| Prova social — resto da frase | Até 6 palavras, completando a oração começada pelo número. A linha inteira tem flex-wrap: quando não cabe, os rostos ficam sozinhos numa linha e o efeito de grupo se perde. |
Promessa (h1) |
6 a 8 palavras, uma oração só, caixa baixa. O max-width:18ch é o que a joga em duas linhas — conte caracteres, não palavras. Cada palavra em um .pitch-w com --pitch-i sequencial a partir de 0. |
| Apoio | 15 a 22 palavras, duas linhas em 44ch. Diz como funciona; não repete o que a promessa já disse. Mesma numeração --pitch-i, reiniciando em 0. |
| Botão | Um só, duas palavras, verbo no infinitivo ("Criar conta", "Começar agora"). Um segundo botão desmonta a dobra: o pop-in de 1.62s existe porque há um alvo, não dois. |
O que mantém a dobra coesa é os quatro tempos dizerem quatro coisas diferentes, nessa ordem: por que confiar (prova), o que é (promessa), como funciona (apoio), o que fazer (botão). Se dois slots dizem a mesma coisa, sobra um tempo vazio na coreografia e a espera de 1.6s até o botão fica sem pagamento. O segundo eixo de coesão é aritmético: --pitch-i sem furo e sem repetição — índice repetido faz duas palavras entrarem juntas e a onda gagueja; índice pulado abre um buraco de 54ms no meio da frase.
Especificação dos assets
São três, e só três: os rostos da linha de prova social.
- Formato e dimensão mínima: WebP quadrado, 112×112 no mínimo. O quadro exibido é 28×28 em CSS — 4× cobre tela de densidade 3 e ainda sobra margem para o recorte.
- Peso alvo: até 8 KB cada. Os três estão acima da dobra e precisam ter pintado antes de o script ligar a transição; um avatar chegando depois entra sem a coreografia.
- Composição / enquadramento: rosto centrado no quadro, ombros cortados, olhar para a câmera. Fundo liso e escuro — o anel de 2px em volta é
--dark, e um fundo claro transforma o avatar numa bola branca a 28px. - O que não pode ter: fundo transparente (o alpha deixa o anel aparecer por dentro do recorte), moldura ou sombra própria, logo, texto, pessoa de corpo inteiro, e principalmente três fotos com temperatura de cor diferente — a 28px o que se lê é a mancha, não o rosto.
- O que a foto não pode ser: cliente real sem autorização de imagem. A linha faz uma afirmação auditável ao lado do rosto de alguém.
Como usar
<link rel="stylesheet" href="componentes/hero/centrado-prova-social/estilo.css">
<script src="componentes/hero/centrado-prova-social/script.js"></script>
Este componente não usa core/motion.js — a entrada inteira é dele (ver Gotchas). Do core ele precisa de tokens.css e de base.css, por causa do .wrap, do .grain e da primitiva .btn.
Ganchos
| Gancho | Papel |
|---|---|
[data-pitch] |
A seção. O script parte daqui, nunca do documento. |
.pitch.in |
Estado que dispara os quatro tempos. Quem põe é o script, e só ele. |
--pitch-title |
Corpo da promessa. clamp(32px,4vw,58px) no padrão. |
--pitch-inset |
Recuo do painel em relação à borda da página. clamp(10px,1.1vw,16px) no padrão. 0 sangra. |
--pitch-i |
Inline em cada .pitch-w. O índice da palavra — é o multiplicador do atraso (54ms no título, 33ms no apoio). |
--pitch-face-pos |
Inline em cada .pitch-face. object-position do recorte. Some ao usar avatar de verdade. |
.pitch-panel |
O painel recuado. Fundo, borda, canto e overflow:hidden. |
.pitch-beams |
As duas faixas de luz. Gradiente, aria-hidden, sem asset. |
.pitch-body |
A caixa centrada. É também o .wrap — a coluna de texto sai dele. |
.pitch-social / .pitch-social-txt / .pitch-social-n |
A linha, o bloco de texto que não pode ser separado dos rostos, e o trecho forte. |
.pitch-faces / .pitch-face |
A fila e cada rosto. |
.pitch-title / .pitch-desc / .pitch-w |
Promessa, apoio e a palavra — a unidade que a coreografia move. |
.pitch-act / .pitch-cta |
O invólucro que estoura e o botão dentro dele. |
Variações
- sem prova social — apague o
<p class="pitch-social">inteiro. Os outros três tempos não mudam de atraso; a dobra apenas abre 0.12s mais tarde do que precisaria. - painel sangrado —
--pitch-inset:0. O fio de fundo em volta some. Acompanhe com--radius-media:0no tema: canto arredondado sem moldura fica sem o que emoldurar. - botão fantasma —
.btn-ghostno lugar de.btn-primary. A escala e o canto que o.pitch .pitch-ctaimpõe continuam valendo; muda só cor e borda. - promessa maior ou menor —
--pitch-title. Persiga a fração de coluna, não o número: a referência ocupa 59% dela. - canto reto —
--radius-media:0no tema. Painel e botão acompanham juntos, porque leem o mesmo token; os rostos continuam redondos, que leem--radius-pill.
Gotchas
-
A entrada é por classe
.inna raiz, e não pordata-rv— num hero,IntersectionObservernão serve. A dobra já está na tela quando a página abre, então o observador dispararia no mesmo quadro em que o CSS pintou o estado inicial, e a transição não teria de onde sair. Por isso oscript.jsfaz uma coisa só: doisrequestAnimationFrameencadeados e depois.in. Com umrAFsó, o navegador às vezes junta os dois quadros e o texto aparece pronto, sem coreografia. É a mesma convenção que ohero/full-bleed-mediaabriu. -
O
setTimeoutde 450ms não é redundância desleixada, é a aba em segundo plano. Onde orAFnão roda — aba aberta com Ctrl+clique, restauração de sessão — o hero ficaria invisível até a pessoa trocar de aba. As duas rotas põem a mesma classe, e pôr.induas vezes não faz nada. Quem tirar osetTimeoutsó descobre o defeito em quem abre links em nova aba, que é exatamente quem chega pela primeira vez. -
A headline é quebrada palavra a palavra aqui, e o
hero/full-bleed-mediadiz no CSS dele para nunca fazer isso. Os dois estão certos. Lá são três palavras com um itálico de destaque, e cortar isso desmonta a força da frase — por isso a reveal dele é por linha, com ooverflowdo.hero-lnfazendo de máscara. Aqui são sete palavras em duas linhas, e a onda de 54ms é a assinatura da dobra: quem trocar por reveal de linha tira o efeito, não o simplifica. -
Não usa
data-splitdo core, e não dá para usar. O gancho quebra o texto em.w, e.wé classe do core — o §6.1 proíbe componente estilizar classe que não começa pelo prefixo dele. Sem poder estilizar.w, não há onde pendurar o--pitch-inem como escalonar o atraso por palavra. Daí os spans virem do markup, com o índice nostyle. O preço é real e é o motivo de o slot da promessa ter regra tão apertada: trocar o texto exige reescrever os spans e renumerar à mão. -
O
.pitch .pitch-ctasobrescreve a primitiva.btn, e o seletor duplo é de propósito..btne.pitch-ctatêm a mesma especificidade; sem o ancestral, quem ganharia seria a folha que carregou por último — e o §6.4 proíbe depender de ordem de carregamento. Mesma solução do.hgal-cap .hgal-type. -
O que ele sobrescreve é escala de tipografia e canto, e isso custa consistência. O
.btnda casa é um rótulo: 11.5px, caixa alta, tracking de .12em, pílula, na--display. O botão desta referência é uma frase: 16px peso 600 na--sans, caixa baixa, canto de--radius-media. Não é gosto — um hero cujo único caminho adiante é este botão pede um alvo que se leia à distância, e o rótulo pequeno some no meio de um painel de 100vh. O custo: numa página que use outros.btn, o do hero será visivelmente de outra família. Aceite isso ou não use a dobra; o que não se faz é editar o.btndo core para caber aqui (§10.5). -
As faixas de luz são dois gradientes, não uma imagem. A referência resolve com um
Bg.webpmais um véu por cima. Asset de terceiro não atravessa (§11) — e mesmo se atravessasse, seria uma foto de 200KB para desenhar duas diagonais a 5% de opacidade. Doislinear-gradient(126deg e 306deg) fazem o mesmo, escalam em qualquer tela e leem token. O grão por cima é o.graindo core, não uma segunda camada nova. -
As faixas usam
color-mixe nãorgbacom número. O §3 proíbe cor crua, ecolor-mix(in srgb, var(--fg) 6%, transparent)é a forma de tirar uma opacidade de um token sem inventar um canal. Trocar porrgba(255,255,255,.06)quebra a dobra em tema claro e passa a acusar nobin/conformidade.py. -
O anel do avatar é
box-shadowe nãoborder. Borda entra na conta da largura e estragaria a sobreposição de −8px, que é justamente o que faz os três rostos lerem como um grupo em vez de três ícones enfileirados. -
O
max-widthda promessa é18ch, não pixels. O que decide a quebra em duas linhas é o número de caracteres, não a largura da tela: trocar a display do tema muda os pixels e mantém a quebra depois de "escale". Quem converter para px vai ver a headline virar três linhas no primeiro projeto com fonte mais larga. -
O apoio é peso 400 e não o 300 que o corpo da página herda. A 24px o Light fica fino demais e o apoio some ao lado de uma headline peso 600 — o contraste de peso entre os dois é o que separa promessa de explicação.
-
A promessa não usa
.display-xldo core, e não é esquecimento. Aquela primitiva é caixa alta com entrelinha .82, feita para título de uma ou duas palavras. Esta headline é caixa baixa, sete palavras e entrelinha 1.07 — são dois desenhos diferentes, e forçá-los numa primitiva só faria as duas piorarem. -
O
paddinglateral do.pitchnão é a coluna: é a moldura do painel. Quem continua controlando a coluna de texto é o.wrap, lá dentro (§4). Quem mexer no--pitch-insetesperando estreitar o texto vai só afastar o painel da borda. -
O painel lê
--dark, não--bg— a dobra é escura por desenho. Trocar.sec-darkpor um escopo claro não a converte: o painel continuaria escuro e o texto dentro dele passaria a ser dark-on-dark. O que o escopo controla é o anel de fundo em volta do painel e a cor do texto; os dois têm de continuar combinando. Se o projeto pede hero claro, esta não é a dobra. -
A dobra entra com blur, e a referência não tem blur. O §5 do contrato é explícito — "nada entra seco" — e é divergência a nosso favor. Vale registrar que o
hero/full-bleed-mediaé anterior a essa regra, o que explica (não justifica) ele transicionar só opacidade e deslocamento. -
No mobile o
min-heightvira0, nos dois: painel e corpo.100vhem celular conta com a barra do navegador recolhida — a dobra fica alta demais e o botão cai abaixo da linha d'água, que é o único erro fatal possível num hero de conversão. Deixar a altura ser o conteúdo é o que garante o botão à vista. Quem devolver100vh"para ficar igual ao desktop" perde o CTA em toda tela pequena. -
O
prefers-reduced-motionentrega a dobra pronta, sem espera nenhuma. Não é um fade mais curto: éopacity:1; filter:none; transform:none; transition:none. Quem pediu menos movimento não deve esperar 1.62s pelo botão. A regra fica colada na que ela desliga, como manda o §6.5. -
Os três rostos do preview não são avatares — são recortes de foto de cena. A biblioteca não tem placeholder de avatar redondo: o preview usa
ph-4,ph-7eph-5(retratos 900×1200 de pessoas em escritório e reunião) puxados para um rosto por--pitch-face-posinline —52% 26%,62% 40%e44% 30%. São enquadramentos escolhidos à mão para esses três arquivos e não significam nada fora deles. Ao trocar por avatares de verdade, apague osstyleinline, senão oobject-positioncontinua deslocando um retrato que já está centrado, e os rostos saem cortados na testa. O preview mente um pouco aqui: com avatares reais a linha fica mais limpa do que ele sugere.
Histórico
| Projeto | Onde | Data | Diferenças |
|---|---|---|---|
| referência: Kirvano | https://www.kirvano.com/ — hero, primeira dobra da home | 2026-09-01 | referência visual, não origem de código |
Origem. Escrito do zero a partir da observação do hero de kirvano.com, medido no navegador a 1440×900. Site em Tailwind, com fonte de terceiro e sem licença declarada: nada foi copiado (§11). O que veio de lá foi a medida e o timing. Status externo — nunca rodou em produção nossa; o primeiro site que usar promove, e esta ficha registra qual foi. A referência está registrada em projetos/referencias-externas.md.
Normalizações aplicadas na tradução:
- Cor crua → token.
#111do painel →--dark;rgba(255,255,255,.1)da borda e as luzes do fundo →color-mix(in srgb,var(--fg) N%,transparent);rgba(255,255,255,.6)ergba(255,255,255,.9)da linha social →--fg-mutede--fg-2;#f5f5f7da headline →--fg;#82d8d0do botão →--accent, e por dentro da primitiva, não escrito aqui. - Asset → gradiente. O
Bg.webpcom véurgba(17,17,17,.8)por cima virou doislinear-gradientinclinados mais o.graindo core. A dobra passou a rodar sem imagem de fundo nenhuma. - Medida fixa → razão e variável.
58px / 62 / −1.45pxvirouclamp(32px,4vw,58px)comline-height:1.07eletter-spacing:-.025em;24px / 33virouclamp(16px,1.6vw,24px)com1.38; as caixas de 672 e 634 viraram18che44ch; omargin:16px 16px 0do painel virou--pitch-inset. Os vãos verticais medidos (28 / 32 / 30) ficaram cravados: são o ritmo da dobra, não parâmetro. - Dois raios → um token. Painel de 16 e botão de 12 viraram os dois
--radius-media. Ver divergências. - Padding lateral →
.wrap. A coluna de texto passou a ser a coluna da casa (§4); o padding do.pitchficou sendo só a moldura do painel. - Primitivas do core no lugar de tipografia própria:
.btn/.btn-primarypara o CTA (com a sobrescrita de escala documentada nos gotchas) e.grainpara o ruído. @keyframes forwards→ transição por estado. Os quatrorise-up/pop-inda referência viraram uma transição só, disparada por.in, com os atrasos preservados. Ganharam o blur que o §5 exige.- Acessibilidade:
aria-hiddennas faixas de luz e na fila de rostos (são decoração),alt=""nos três<img>,decoding="async"para não travar o primeiro quadro, e o<h1>continua uma frase inteira para leitor de tela mesmo quebrado em spans. O desligamento em movimento reduzido a referência também tem — aqui ele mora colado na regra que desliga (§6.5).
Divergências conhecidas vs. a referência:
- A fonte não atravessa. A referência roda Inter Tight em texto e headline (e uma display proprietária em outra dobra). O §1 proíbe Google Fonts em produção e o §11 proíbe fonte de terceiro sem licença: o tema
kirvanonão tem@font-facee cai no grotesco do sistema, que é o parente mais próximo. Consequência prática: o18chfoi calibrado para manter a quebra depois de "escale", não os pixels — ao receber os arquivos licenciados, confira a quebra antes de mexer no corpo. - O botão segue a primitiva
.btnda casa. Forma, hover, transição e estados vêm dela; o que este componente muda é escala de tipografia e canto, e nada mais. Efeito colateral do hover herdado do.btn-primary: ele vira fundo translúcido combackdrop-filter, e não o escurecimento do original. - O raio do botão é 16 e não 12. A referência tem 12px no botão e 16px no painel. Aqui os dois leem
--radius-media— quatro pixels a mais, de propósito, para o botão e o painel terem o mesmo canto. Um token novo só para o botão seria decisão de arquitetura (§9.3) por causa de uma dobra. - O menta
#82d8d0não é token nosso. Entra como--accentdo temakirvano. Em qualquer outro tema o botão sai na cor de marca daquele cliente e a dobra continua correta — que é exatamente o ponto do §10.5. - O preview usa o tema
kirvano, e os vizinhos não. Os demais previews da biblioteca pintam o tema padrão — sage e canto vivo. Este carregacore/temas/kirvano.css(menta, creme,--radius-media:16px) porque o painel recuado com canto arredondado é metade do que a dobra é; com canto vivo ela lê como uma seção comum. Quem comparar este preview lado a lado com os outros vai ver duas casas diferentes: é o tema, não o componente. - Não existe placeholder de avatar redondo na casa, então os três rostos do preview são recortes de foto de cena. Ver o último gotcha.
- O stagger é o da referência, não o do core. 54ms na headline e 33ms no apoio; o
data-rvdo core escalona a 48ms. Como esta dobra não usa o gancho do core, os dois números convivem sem conflito. - O mockup de dashboard ficou de fora. Abaixo do botão a referência tem uma captura de interface inteira. Está fora do escopo desta dobra — e seria asset de terceiro de qualquer forma.