Um cabeçalho que se abre em duas pontas — título à esquerda, apoio encostado na margem direita — e, abaixo dele, uma esteira de cards brancos que atravessa a página sem parar. Cada card é retrato redondo de 44px, nome, área de atuação e a citação embaixo. As duas pontas do trilho desvanecem em vez de cortar, então a fila não tem começo nem fim visível: o olho pega a esteira no meio, lê um card, e o próximo já entrou. Passar o ponteiro para a fila, e é a única navegação que existe aqui.
Resolve o problema do depoimento que ninguém lê. Empilhado em grade, um bloco de três citações vira parede de texto e o visitante pula; num carrossel com setas, ele teria de decidir clicar — e ninguém procura o terceiro depoimento de uma empresa. A esteira tira a decisão do caminho: ela oferece movimento e devolve legibilidade só a quem pediu, parando no hover. E como o trilho não tem borda, a dobra afirma "há mais gente aqui" sem prometer um número que uma lista completa teria de sustentar.
Quando usar
- Dobra de prova social com citação de pessoa real, com nome e rosto — página de assessoria, consultoria, qualquer serviço de relação longa.
- 3 a 6 depoimentos. Três é o mínimo para o laço não ficar óbvio; acima de seis a volta fica longa demais para alguém esperar o card que passou.
- Quando o argumento da dobra é quantidade de gente satisfeita, e não um case específico com número.
- Quando existe retrato de verdade de cada pessoa. Depoimento sem rosto lê como texto escrito pelo marketing.
Quando não usar
- Menos de três depoimentos: a repetição aparece na primeira volta. Uma citação só, grande e centrada, é
conteudo/manifesto-citacao. - Quando o depoimento tem de levar a um case, um vídeo ou uma página de cliente. Aqui o card não é link e não pode virar um sem quebrar a esteira — use
midia/galeria-scroll-horizontal, em que o card é um<a>de verdade e o encaixe é nativo. - Quando a pessoa importa mais que a frase — sócio, conselheiro, especialista, com bio longa e gesto de abrir:
midia/carrossel-perfis. Lá o arrasto é o ponto, e a bio fica guardada atrás de um clique; aqui não há arrasto, não há estado aberto e a citação está inteira desde sempre. - Quando a prova é logo de empresa e não frase de pessoa:
prova/marquee-logos. É a mesma esteira, com item de 26px de altura e sem texto para ler — por isso ela pode correr rápido e esta não pode. - Quando o argumento é número (faturamento, volume, tempo de casa):
prova/stats-contador-regua. - Quando as citações precisam ficar paradas para serem comparadas lado a lado — três planos, três perfis de cliente. Aí é grade:
conteudo/grid-entidades.
Padrão editorial
| Slot | Regra |
|---|---|
Título (h2.sec-title.ttl) |
5 a 8 palavras, uma ou duas linhas na caixa de 560px. Diz o que as pessoas fizeram, não que elas são clientes. |
Apoio (.depo-head-right) |
Duas linhas, separadas por <br> escrito à mão — a caixa tem 470px e alinha à direita, então quebra automática deixa a segunda linha caindo torta. Enquadra o conjunto ("de quem começa a quem já fatura milhões"); não repete o título. |
| Retrato | Um por card, obrigatório. Rosto reconhecível a 44px. |
| Nome | Nome e sobrenome. Sem título acadêmico, sem @, sem nome de empresa colado. |
Área (.depo-role) |
Uma linha, até ~28 caracteres. É o perfil, não o cargo: "Empresário — indústria", "Família — terceira geração". Cargo de empresa que ninguém conhece não prova nada. |
| Citação | 220 a 340 caracteres. Abaixo disso o card fica com um vão branco embaixo, porque todos têm a mesma largura de 340px e a altura acompanha o mais alto da fila. Acima de 340 a citação passa de sete linhas e o card cresce sozinho, desalinhando o resto. |
| Quantidade | 3 a 6 depoimentos, escritos duas vezes no markup (ver Gotchas). |
O que mantém a fila coesa é o paralelismo de tamanho, e ele é mais rígido aqui do que em componente de grade: como os cards passam um atrás do outro na mesma linha, uma citação de duas linhas ao lado de outra de sete lê como card quebrado, não como variação. Escreva as três no mesmo comprimento e corte o excesso — depoimento cortado com reticências é aceitável e comum; card desigual não é.
Ordem é regra editorial, não gosto. Com prefers-reduced-motion: reduce a esteira congela na posição inicial, e quem tem essa preferência ligada vê exatamente os primeiros cards e mais nada. Os três primeiros depoimentos têm de ser os mais fortes do conjunto — o nome mais conhecido, a frase mais específica, o segmento mais próximo do público da página. O quarto em diante é bônus para quem espera a volta.
Especificação dos assets
- Formato e dimensão mínima: WebP quadrado, 240×240. O quadro exibido é 44×44 com
object-fit:cover, então 176×176 já cobre 4×; 240 dá margem para o dia em que o card crescer. - Peso alvo: até 25 KB por retrato. São 3 a 6 arquivos, e cada um aparece duas vezes no DOM — a segunda cópia leva
loading="lazy", mas o navegador reaproveita a mesma URL, então o custo real é de um download por pessoa. - Composição / enquadramento: rosto ocupando pelo menos metade da altura do quadro, olhando para a câmera. A 44px, plano americano vira mancha. Cada card ajusta o próprio corte por
--depo-face-posnostyle— retrato de estúdio pede algo como50% 20%, foto de ambiente pede o centro. - O que não pode ter: logo de empresa no lugar do rosto; recorte em fundo branco misturado com foto de ambiente (a 44px, dentro de um card branco, o recorte some e a foto de ambiente vira um quadrado escuro); moldura, sombra ou borda no próprio arquivo — o círculo vem do CSS; captura de tela de perfil de rede social com marca d'água.
Como usar
<link rel="stylesheet" href="componentes/prova/carrossel-depoimentos/estilo.css">
Sem script. O core (tokens.css, base.css, motion.css, motion.js) já é o que faz o cabeçalho entrar.
Estrutura obrigatória
O .depo-track contém dois .depo-set idênticos. O primeiro é o conteúdo; o segundo leva aria-hidden="true", alt="" nas imagens e loading="lazy". Acrescentar um depoimento significa acrescentá-lo nos dois conjuntos, na mesma posição.
O cabeçalho fica dentro do .wrap; a esteira é irmã dele, filha direta da <section> — é assim que ela sangra sem margem negativa.
<section class="sec-light sec-pad depo">
<div class="wrap"> <div class="depo-head"> … </div> </div>
<div class="depo-rail" data-rv data-d="2">
<div class="depo-track">
<ul class="depo-set"> … 3 cards … </ul>
<ul class="depo-set" aria-hidden="true"> … os mesmos 3 … </ul>
</div>
</div>
</section>
Ganchos
| Gancho | Papel |
|---|---|
.depo |
A seção. Carrega as quatro variáveis de ajuste — é o único lugar onde se mexe. |
.depo-head / .depo-head-left / .depo-head-right |
Cabeçalho de duas pontas. A esquerda tem max-width:560px; a direita, 470px e text-align:right. |
.depo-rail |
O recorte. Dono da máscara das pontas e do padding-inline que alinha o primeiro card à coluna. |
.depo-track |
O que anda. Um flex de width:max-content com a animação depoRun. |
.depo-set |
Um conjunto. Tem de haver exatamente dois, idênticos. |
.depo-card / .depo-who / .depo-face / .depo-id / .depo-name / .depo-role / .depo-quote |
Card e suas partes. |
--depo-card |
Largura do card. 340px no padrão; min(80vw,320px) abaixo de 760px. |
--depo-gap |
Vão entre um card e o próximo. 20px no padrão, 14px no mobile. |
--depo-dur |
Uma volta inteira. 44s no padrão, para três citações. Cresce junto com o número de cards (ver Gotchas). |
--depo-inset |
Recuo que alinha o primeiro card à borda da coluna: max(--pad, (100% − --max)/2 + --pad). Refaz a conta do .wrap. Não mexer sem motivo. |
--depo-face-pos |
Corte do retrato, por card, no style do <img>. Padrão 50% 30%. |
Classes de estado: nenhuma. O componente não tem .is-* porque não tem JavaScript. O único estado é :hover na raiz .depo, que pausa a animação — não há classe para inspecionar nem para forçar.
Variações
- mais ou menos depoimentos — acrescente pares de cards nos dois
.depo-sete suba--depo-durna mesma proporção: a conta é--depo-dur ≈ 14s × número de cards do conjunto. Seis cards pedem~88s. - esteira mais lenta ou mais rápida — só
--depo-dur. Abaixo de30spara três cards a citação passa antes de ser lida; acima de60sa dobra parece parada e o hover deixa de ter sentido. - cards mais estreitos, quatro na tela —
--depo-card:280px. Aí a citação cai para 180–260 caracteres, senão o card cresce em altura. - em seção escura — troque
.sec-lightpor.sec-deepno markup. Nome, citação, área e o fio da borda leem tokens de escopo e acompanham sozinhos; obackground:var(--light)do card, não — ele continua branco de propósito, que é o contraste que faz o card flutuar sobre o escuro. Se quiser card escuro sobre escuro, é variação nova na ficha, não ajuste local. - esteira parada —
--depo-durnão resolve (não existe duração infinita); apague a linhaanimationdo.depo-trackou aplique o mesmoanimation:nonedo bloco de movimento reduzido. Parada, a dobra vira uma fileira que sangra à direita, e aí o candidato certo é outro componente.
Gotchas
-
A lista de depoimentos está escrita duas vezes no HTML, e isso não é descuido. A animação anda
translateX(-50%), que é exatamente a largura de um conjunto — é isso, e só isso, que faz o quadro final coincidir pixel a pixel com o inicial e o laço fechar sem salto. Se alguém "limpar" o markup deixando um conjunto só,-50%passa a ser metade de três cards e a fila dá um solavanco visível a cada volta. Mesma receita doprova/marquee-logos, pelo mesmo motivo. A segunda cópia é decorativa e levaaria-hidden="true"comalt="": sem isso o leitor de tela anuncia cada pessoa duas vezes, o que num bloco de prova social soa como se a empresa tivesse inventado clientes. -
O
padding-inline-endno.depo-setparece sobra e é a emenda. Ogapsepara os cards dentro de um conjunto, mas não separa o último card do primeiro do conjunto seguinte. Sem esse padding, os dois conjuntos se encostam e a única costura da esteira fica sendo justamente o ponto em que dois cards se tocam — a falha aparece uma vez por volta, sempre no mesmo lugar, e é a primeira coisa que alguém nota. Ele tem de valer exatamente--depo-gap; qualquer outro valor troca o solavanco por um vão maior no mesmo ponto. -
A máscara horizontal das pontas é a primeira da biblioteca nesse eixo. As outras dez ocorrências de
mask-imageno repositório são todas o véu vertical sobre foto do §6 do contrato — outra receita, outro problema. Esta resolve um corte:overflow:hiddensozinho secciona o card no meio com uma borda dura e o olho lê "quebrou", não "continua". As paradas em 5% e 95% são as da referência. Se um terceiro componente pedir esta mesma máscara horizontal, ela deixa de ser caso isolado e vira candidata a primitiva do core (§6: regra que aparece em três componentes se promove antes de recopiar pela terceira vez). -
padding-block:8pxno.depo-railnão é respiro, é o que salva o anel de foco.overflow:hiddencorta nos dois eixos, não só no horizontal: sem esses 8px, ooutlinede foco do card e qualquer sombra ficariam decapitados em cima e embaixo. Quem tirar por achar que a esteira está "flutuando" só descobre o efeito ao navegar por Tab. -
A sangria é
--depo-inset, não margem negativa. O contrato §4 é explícito: componente não mexe empadding-inlinede página nem ganhamarginlateral negativa; quem precisa vazar sai de dentro do.wrap. É o que a esteira faz — ela é irmã do cabeçalho, filha direta da seção, e por isso já nasce com a largura da página. O que--depo-insetfaz é o contrário de sangrar: refaz a conta do.wrap(max(--pad, (100% − --max)/2 + --pad)) para o primeiro card começar alinhado com o título, e não colado na borda da tela. Mesmo recurso domidia/galeria-scroll-horizontal. Trocar pormargin-inline:calc(var(--pad) * -1)funciona em desktop e produz barra de rolagem horizontal na página em telas estreitas. -
O cabeçalho não usa
.sec-headdo core, e é de propósito..sec-headé cabeçalho centrado, com largura por--head-max. Aqui os dois blocos precisam se olhar de ponta a ponta da coluna — o título ancorado à esquerda e o apoio alinhado à direita, o que é justamente o que abre a largura visual da dobra antes de a esteira começar. Não é primitiva ignorada; é outra forma. -
text-align:rightsai no mobile. Abaixo de 760px o cabeçalho empilha, e apoio alinhado à direita embaixo de título alinhado à esquerda descola os dois blocos: viram dois elementos soltos em vez de um cabeçalho. A media query devolvetext-align:lefte solta omax-width. -
Com movimento reduzido a esteira congela onde nasceu — não vira grade, não empilha, não ganha setas. Só
animation:none. A alternativa seria reorganizar a dobra num layout paralelo, e aí o componente passaria a ter duas formas para manter. O preço dessa decisão é editorial e está pago na seção de Padrão editorial: os três primeiros depoimentos são os que essa pessoa vai ver, então são os melhores. Quem trocar a ordem por conveniência (ordem alfabética, ordem de contrato) quebra essa garantia sem quebrar nada visível. -
O card não é focável e não é link. Um
tabindexaqui daria foco a um alvo que se move sozinho e que existe em duplicata no DOM. Se o depoimento precisar levar a algum lugar, o componente é outro — ver "Quando não usar". -
A pausa no hover é a única navegação, e basta. Não há seta, não há indicador, não há arrasto. Depoimento não se navega, se atravessa: ninguém abre uma página para procurar o terceiro depoimento. Se a dobra parecer precisar de controles, o sinal é que as citações estão longas demais ou em número demais — corte antes de acrescentar UI. A pausa não funciona em toque (não há hover), e isso é aceitável: no mobile o card ocupa 80vw e passa devagar o suficiente para ser lido inteiro.
Histórico
| Projeto | Onde | Data | Diferenças |
|---|---|---|---|
| referência: Kirvano | https://www.kirvano.com/ — dobra "Histórias de quem cresce com a Kirvano" | 2026-09-01 | referência visual, não origem de código |
Escrito do zero a partir da observação da dobra de depoimentos de kirvano.com, medida no navegador a 1440×900. O site roda Tailwind, com fonte de terceiro e sem licença declarada: nada foi copiado (§11). O que veio de lá é a medida e a ideia da interação. Números lidos na referência e reproduzidos aqui: card 340 × 268, gap 20, padding 24, retrato 44×44, nome 16/24 peso 600, área 14/20, citação margin-top 16, 16/26; máscara linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent); cabeçalho de duas colunas com esquerda em 560 e direita em 470 alinhada à direita; três depoimentos duplicados ×2, fila de 2140 de largura — que é exatamente 340×6 + 20×5, a conta que este componente refaz por variável. Status externo: nunca rodou em produção nossa. O primeiro site que usar promove, e a ficha registra qual foi. A referência está registrada em projetos/referencias-externas.md.
Normalizações aplicadas na tradução:
- Cores cruas viraram token de escopo: fundo
#f1ece1→.sec-light(o creme é do tema, não do componente); card#fff→--light; bordargba(41,41,41,.1)→--rule; nome#292929→--fg; citação#5b5b5b→--fg-2; área#757575→--fg-muted. O componente não escreve uma cor. - Raio do card
16px→--radius-media, e o círculo do retrato →--radius-pill. Nenhum número de forma fica cravado. - Medidas fixas viraram variável na raiz:
340→--depo-card,20→--depo-gap, e a volta inteira →--depo-dur. Os vãos do cabeçalho viraramclamp(clamp(20px,3vw,32px)eclamp(30px,5vh,48px), este último com teto no 48 medido). - O movimento saiu do JavaScript. Na referência a fila é um ticker que reescreve
transforminline a cada quadro; aqui é uma animação CSS detranslateX(0)a-50%, e o arquivoscript.jssimplesmente não existe. Menos código, e oprefers-reduced-motiondesliga na própria folha. - Sangria refeita pela regra da casa (
--depo-inset), sem margem negativa e sem100vw(§4). - Título e entrada vêm do core:
h2.sec-titlecom.ttl, apoio e trilho comdata-rv/data-d. A referência entra comrise-up, que é fade + subida sem blur; o §5 do contrato exige blur em toda entrada, e o gancho do core já entrega isso. - Acessibilidade que o original não tinha explícita: segunda cópia com
aria-hidden="true"ealt="",loading="lazy"só nas duplicatas,decoding="async"em todas, ealtdescritivo ("Retrato de …") no conjunto que conta. - Corte do retrato virou contrato de dado (
--depo-face-pospor card), como noconteudo/pilares-texto-na-foto, em vez de umobject-positionúnico para todo mundo.
Divergências conhecidas vs. a referência:
- Inter Tight não atravessa. É fonte de terceiro sem licença declarada, e o §1 ainda proíbe Google Fonts em produção. O componente lê
--sans/--displaydo tema; ocore/temas/kirvano.cssdeclara a pilha com Inter Tight no topo e cai no grotesco do sistema enquanto não houver arquivo licenciado. O desenho não depende disso — o que muda é a textura do texto, não a caixa. - Nenhuma cor de marca entra nesta dobra, e não é esquecimento. A referência não tem CTA aqui, e o componente não lê
--accent: o menta#82d8d0não aparece em card, borda ou retrato. Onde a família de clones desta referência tem botão, ele é a primitiva.btn/.btn-primarydo core, que lê--accentdo tema e não recebe retoque de forma — este é o componente da família que não tem nenhum. - O ritmo vertical é maior que o do original. A seção da referência tem
padding:96px 0; aqui é.sec-pad, que lê--sec-gap(clamp(110px,14vh,160px)). O contrato manda uma fonte única para o que separa dobras (§4), e umclampsolto de 96px seria exatamente o que ele proíbe. Na prática a dobra respira mais que a original. - A velocidade é escolha nossa. A medição não registrou o período do ticker da referência;
--depo-dur:44spara três cards foi calibrado pela leitura — tempo de atravessar uma citação de ~300 caracteres antes de o card sair de cena. - O preview roda no tema
kirvano, e não no tema padrão da biblioteca.bin/previews.shgera tudo emliberta-wealth, que pinta sage (--accent:#c6ceaa), creme#ede9e3e canto vivo (--radius-media:0). Neste componente opreview-tema.txtfixakirvano, que traz o creme#f1ece1e o raio de 16px da referência — sem isso o card sairia retangular e a dobra deixaria de parecer com o que foi medido. Abrir os dois previews lado a lado mostra dois desenhos diferentes do mesmo arquivo: é o §10.5 funcionando (edita-se o tema, nunca o componente), não um preview quebrado. - Os retratos são placeholders. As fotos e os nomes da referência são de pessoas reais e não atravessam (§11); o preview usa
ph-3,ph-4eph-7deshowroom/demo/com nomes fictícios. O corte de cada um está calibrado à mão nostyle— ao trocar pelas fotos reais, recalibre--depo-face-poscard a card.