Painéis de foto lado a lado. O ativo ocupa 3 a 5× a largura dos outros e revela o conteúdo inteiro; os fechados mostram só um rótulo girado na vertical. Clique, toque, foco ou seta trocam o painel aberto — o hover só acende o painel fechado, sem abri-lo.
Resolve o problema clássico de "cinco blocos iguais, nenhum lido": aqui só um bloco de texto existe por vez, então o olho tem um destino.
Dois modos, um motor:
| Modo | Classe | O painel aberto carrega | O fechado mostra |
|---|---|---|---|
| Serviço (original, 2026-08) | — | número, título e uma frase de benefício | número + nome do serviço na vertical |
| Depoimento (2026-09) | .fan-depo |
aspas, citação em corpo grande, nome, idade/relação, linha de contexto e contador NN / NN |
nome da pessoa na vertical + sinal +/− |
A mecânica é a mesma nos dois: um aberto por vez, a mesma curva de 1,1s, o mesmo véu com máscara, o mesmo teclado. O que muda é conteúdo e três medidas, e as três são parafuso do próprio componente. O modo depoimento ainda aceita rodízio (data-fan-auto), com anterior / pausar / próximo.
Compliance
Depoimento em site de assessoria não é texto, é declaração. Esta seção não é recomendação: é o que tem de estar resolvido antes de a peça ir ao ar, e é por isso que ela vem antes de tudo na ficha.
1. Retrato ilustrativo tem de se declarar — na peça
Se o rosto do painel é gerado por IA ou de banco de imagem, a peça diz isso, com a primitiva .nota do core, colada ao leque:
<p class="nota fan-nota">Relatos e retratos ilustrativos, criados para demonstrar a peça — as imagens são de banco de imagem e não retratam clientes. Não são depoimentos de clientes da casa.</p>
Não vale no rodapé da página. A ressalva mora no .fan-foot, no mesmo bloco visual do leque, e é isso que faz dela parte da leitura em vez de letra miúda jurídica. Quem rola até o depoimento lê a ressalva junto; quem vai embora antes do rodapé nunca a veria.
2. Relato ilustrativo não é depoimento de cliente
Texto escrito pela agência para mostrar a forma não pode ser apresentado como fala de alguém. Duas travas no markup de exemplo, e as duas são exigíveis em produção:
- a ressalva diz, com estas palavras, que não são depoimentos de clientes;
- o nome é nome + inicial (
Marina A.), que é a forma que não se confunde com pessoa identificável.
3. Depoimento real tem três exigências
| Exigência | Por quê |
|---|---|
| Autorização de uso de imagem e de voz, assinada, guardada e com prazo | o rosto e a frase são dados pessoais; publicar sem autorização é exposição, não prova |
| Nenhuma menção a rentabilidade, retorno, percentual ou valor de patrimônio | fala de cliente sobre resultado é publicidade de resultado, e resultado passado não é promessa de futuro — nem quando quem diz é o cliente |
| A relação declarada (é cliente? há quanto tempo? recebeu algo por isso?) | depoimento pago ou de parte relacionada sem declaração é propaganda disfarçada |
4. O exemplo é inofensivo por construção
As quatro citações deste markup foram escritas para continuarem publicáveis mesmo se fossem verdadeiras. Todas falam de processo — clareza, organização, sucessão, o "não" que alguém ouviu do outro lado da mesa — e nenhuma cita número, produto, percentual ou patrimônio. Ao substituir por texto real, esse é o teste: se esta frase fosse de um cliente identificado, ela poderia ir ao ar? Se a resposta depende de a frase ser fictícia, a frase está errada.
Nada de marca de terceiro. Nem banco, nem corretora, nem gestora — nem na citação, nem na linha de contexto, nem no .fan-rel.
Quando usar
- 4 a 6 serviços, soluções ou verticais de atuação — cada um com foto própria.
- 4 a 6 depoimentos com retrato, quando cada um tem uma frase e um rosto e a página quer que se leia um de cada vez.
- Quando as descrições são curtas e equivalentes em peso (nenhum item é o principal).
- Seção de meio ou de segunda metade de página, depois que o visitante já sabe quem é a empresa.
Quando não usar
- Menos de 4 itens. Sobra largura, os painéis fechados ficam gordos e o efeito de leque some.
- Mais de 6. O painel fechado fica estreito demais para o rótulo vertical, que passa a truncar.
- Itens com texto longo ou com sub-listas — o
.fan-fulltem largura fixa. - Quando os itens precisam ser comparados lado a lado (preço, plano, pacote). Leque esconde; comparação exige mostrar tudo junto.
- Se não houver foto boa para cada item. Painel com foto fraca destrói a fileira inteira.
O irmão mais próximo, e quando ele ganha: prova/carrossel-depoimentos. Ele é uma esteira que não para, com cards de citação e retrato pequeno — e ganha quando os depoimentos são muitos e curtos, quando não há foto grande por pessoa, e quando a dobra quer afirmar "há muita gente aqui" sem obrigar ninguém a escolher. O leque ganha no caso oposto: poucos relatos, cada um com um retrato que merece ocupar meia dobra, e a leitura de um por vez como parte do argumento. Regra curta: se o rosto é conteúdo, leque; se o volume é o conteúdo, esteira.
Padrão editorial
Comum aos dois modos
| Slot | Regra |
|---|---|
| Olho | 2 a 4 palavras: "O que oferecemos", "Histórias de quem é assessorado". |
| Título da seção | 1 linha com uma palavra em <em>. |
.fan-vert |
O rótulo do painel fechado. No modo serviço, o nome do serviço em 2 a 3 palavras, quebrado com <br> em até 2 linhas; no modo depoimento, o nome da pessoa, 1 a 2 palavras, sem <br>. Se não couber na vertical, o nome é longo demais. |
Só no modo serviço
| Slot | Regra |
|---|---|
.fan-num |
Sequência 01…05. Não pule número, não use ícone no lugar. |
.fan-title |
Repete o nome do serviço por extenso, sem <br>. |
.fan-text |
1 frase, 18 a 30 palavras, teto de 40ch de largura. Diz o benefício, não o processo. |
Só no modo depoimento
| Slot | Regra |
|---|---|
.fan-say |
A citação. 18 a 34 palavras, 1 a 2 frases, na voz da pessoa. Compliance: nada de rentabilidade, retorno, percentual ou valor de patrimônio. Medido a 1440: a coluna dá 492px, ou 32ch de corpo display, e 25 palavras ocupam 3 linhas; acima de 34 palavras passa de 5 e a troca de painel fica pesada. |
.fan-who |
O nome, igual ao do .fan-vert. Relato ilustrativo usa nome + inicial. |
.fan-rel |
Idade e vínculo, até 44 caracteres: 54 anos — família em segunda geração. É o perfil, não o cargo, e nunca o nome de uma empresa real. |
.fan-ctx |
Uma frase de 4 a 9 palavras dizendo de que conversa a citação saiu. Sem ela o relato fica no ar. |
.fan-count |
NN / NN — posição e total, os dois com zero à esquerda. Escrito no markup, não pelo script. |
.fan-nota |
A ressalva de origem. Obrigatória. Veste .nota do core (as duas classes no mesmo elemento). Ver Compliance. |
Consistência é o que sustenta o efeito. Se um item tem 12 palavras e outro tem 40, a troca de painel fica desigual. Escreva os quatro a seis textos juntos, no mesmo fôlego.
Especificação das imagens
- Retrato ou quadrada, mínimo 1200×1600. O painel fechado é estreito e alto.
- Assunto centralizado na horizontal — as bordas são cortadas quando o painel fecha.
- Até 250 KB cada. São 4 a 6 fotos na mesma dobra.
- Já entram em
grayscale(1) brightness(.45): a cor só aparece parcialmente no painel aberto. Escolha fotos com contraste de forma, não de cor. - No modo depoimento, o enquadramento é de rosto, e o rosto precisa sobreviver ao corte do painel fechado — que é uma faixa vertical estreita. Retrato com a cabeça fora do eixo central some quando o painel fecha.
Como usar
<link rel="stylesheet" href="componentes/listas/leque-paineis/estilo.css">
<script src="componentes/listas/leque-paineis/script.js"></script>
O markup.html traz duas seções: o modo serviço e a variação de depoimento. Cole uma, nunca as duas — são a mesma forma com conteúdo diferente. (As duas juntas funcionam tecnicamente: o script parte de cada [data-fan] e mais de um leque na mesma página não conflita.)
Modo serviço — nada mudou
<div class="fan" data-fan>
<article class="fan-panel is-open" tabindex="0">…</article>
…
</div>
[data-fan] no próprio .fan, sem invólucro, sem controles. É o markup de 2026-08 linha por linha, e continua sendo o caminho padrão.
Modo depoimento, com rodízio
<div class="fan-box" data-fan data-fan-auto data-rv data-d="1">
<div class="fan fan-depo">
<article class="fan-panel is-open" tabindex="0">
<img class="fan-img" …><div class="fan-veil"></div>
<span class="fan-sign" aria-hidden="true"></span>
<div class="fan-vert">Marina A.</div>
<div class="fan-full">
<div class="fan-top">
<span class="fan-mark" aria-hidden="true">“</span>
<span class="fan-count">01 / 04</span>
</div>
<blockquote class="fan-say">…</blockquote>
<p class="fan-who">Marina A.<span class="fan-rel">54 anos — …</span></p>
<p class="fan-ctx">Sobre a primeira reunião de diagnóstico.</p>
</div>
</article>
…
</div>
<div class="fan-foot">
<p class="nota fan-nota">…a ressalva de compliance…</p>
<div class="fan-ctl" data-fan-ctl hidden>
<button class="btn-disc" data-fan-prev …> <!-- ‹ -->
<button class="btn-disc btn-play" data-fan-play hidden
data-rot-pausar="…" data-rot-tocar="…">
<button class="btn-disc" data-fan-next …> <!-- › -->
</div>
</div>
</div>
Três coisas que valem a leitura:
[data-fan]sobe para o.fan-boxsó porque agora existe um rodapé (ressalva + controles) que precisa estar dentro da raiz para o script encontrá-lo. Sem rodapé,[data-fan]continua podendo morar no próprio.fan.data-fan-autoé opt-in. Sem ele o modo depoimento se comporta igual ao de serviço: abre por clique, foco e seta, e nada anda sozinho.- Os botões nascem
[hidden]dentro de uma barra[hidden]. Quem mostra a barra é o script; quem mostra o botão de pausa é oLIB.pausaveldo core.
Ganchos
| Gancho | Papel |
|---|---|
[data-fan] |
raiz do leque — o .fan ou o .fan-box |
[data-fan-auto] |
liga o rodízio. Ausente = nada anda sozinho |
.fan-depo |
modo depoimento, no bloco raiz (.fan) |
.fan-panel.is-open |
painel ativo — marque um no HTML |
tabindex="0" no painel |
obrigatório: é o que dá navegação por teclado |
[data-fan-ctl] |
a barra de controles; nasce [hidden] |
[data-fan-prev] / [data-fan-next] |
anterior / próximo. Funcionam sempre, inclusive com movimento reduzido |
[data-fan-play] |
o .btn-disc.btn-play do core. Nasce [hidden]; quem o mostra é o LIB.pausavel |
Parafusos
Todos são custom property do próprio componente, declarados no bloco raiz — nenhum é token global (§3 do contrato). Sobrescreva-os no tema ou numa classe de página, nunca editando o componente.
| Parafuso | Padrão | O que faz |
|---|---|---|
--fan-open |
3.4 (.fan-depo: 4.6) |
o flex do painel aberto. Com 4 painéis de serviço, 4.2; com 6, 3 |
--fan-full-w |
min(440px,84vw) (.fan-depo: min(560px,86vw)) |
a largura fixa do bloco de texto. É ele que manda na medida da linha |
--fan-say |
clamp(18px,2vw,24px) |
o corpo da citação. Só usado no modo depoimento |
--fan-auto |
6s |
o período do rodízio, lido do CSS pelo script |
--nota-size / --nota-max |
do core | tipografia da ressalva |
Variações
- Modo depoimento —
.fan-depono bloco raiz. Documentado acima. - Rodízio automático —
data-fan-autona raiz, com a barra de controles. - Proporção da abertura —
--fan-open. Antes de 2026-09 isto exigia editar o CSS do componente, que é o que o §10.5 do contrato proíbe. - Sem numeração — apague
.fan-num. Funciona, mas perde a leitura de "conjunto ordenado". - Canto arredondado — vem de
--radius-media, do tema. Projeto flat entrega 0.
Gotchas
.fan-fulltem largura fixa, não porcentagem. Com porcentagem o texto reflui enquanto o painel encolhe e a saída fica sanfonada. Não "conserte" isso — e é por causa dela que--fan-opene--fan-full-wandam juntos: o painel aberto tem de ser mais largo que o bloco de texto, senão a citação encosta na borda e o painel deixa de parecer foto com legenda. Medido a 1440 com 4 painéis:--fan-open:3.4dá painel de 570px contra texto de 560 — 10px de foto.4.6dá 649px e devolve 89px.- A saída é rápida e a entrada é atrasada (
.18scontra.5s .22s). É proposital: o texto antigo tem que sumir antes de o novo chegar, senão os dois se sobrepõem no meio da transição. - O véu precisa da máscara em gradiente.
backdrop-filterem caixa reta corta a foto no meio com borda dura. Amask-imageestá no CSS — se sumir, o painel lê "chapado". - O leque é uma ilha escura, e por isso lê
--accente não--accent-ink. O §3 manda o realce ler--accent-ink, e está certo para texto que vive no fundo da seção. Aqui o número e as aspas vivem sobre foto emgrayscale(1) brightness(.45), que é escura em qualquer tema — numa dobra.sec-papero--accent-inkviria já misturado com a tinta clara do escopo e sairia lavado sobre a foto. Quem está fora dos painéis (a barra de controles e a ressalva) lê os tokens do escopo normalmente, e é por isso que os dois trocam de cor junto com a seção. height:min(62vh,520px)— em notebook de 768px de altura o leque ocupa 62% da tela e ainda sobra respiro. Não troque porvhpuro: em monitor 4K vira uma parede.- Hover não abre, e isso é regra, não esquecimento (CLAUDE.md §6, 2026-09-29). Até então o
mouseenterabria o painel: a foto crescia e a fileira inteira se rearranjava só porque o ponteiro passou por cima a caminho de outra coisa. Agora o hover só acende o painel fechado, e quem abre é o clique, o toque, o foco ou a seta. - Não há estado "nenhum aberto" — por design. Sem
.is-openno markup, o script abre o primeiro. - O ponteiro sobre um painel pausa o rodízio, e o ícone acompanha. Isso significa que, passando o mouse pelo leque, o botão vira "retomar" e volta a "pausar" quando o ponteiro sai. É chato de ver e é a decisão certa: um leque parado com ícone de "pausar" na tela seria um botão mentindo sobre o próprio estado. O efeito colateral é que não dá para pausar de propósito com o ponteiro sobre um painel — mas a barra de controles fica fora do leque, então basta mover o ponteiro até ela (o rodízio retoma no caminho) e clicar.
- Foco num painel pausa; foco nos três botões, não. Sem essa distinção o próprio botão de retomar seguraria o relógio parado — o foco nele bastaria para o rodízio nunca voltar.
- Sem
LIB.pausavelnão há rodízio. Componente colado sem ocore/motion.jscontinua funcionando como leque manual e o relógio simplesmente não liga. É deliberado: a WCAG 2.2.2 exige um jeito explícito de parar o que anda sozinho, e o relógio só pode andar onde existe o controle que o para. - O contador é escrito no markup. Acrescentar um depoimento significa renumerar todos (
01 / 05,02 / 05…). O script não toca nele de propósito: sem JavaScript a peça continua dizendo quantos relatos existem. - Todos os textos dos painéis estão no DOM o tempo todo, com
opacity:0nos fechados. É o que faz a peça funcionar sem JavaScript, e é também o que faz um leitor de tela anunciar as quatro citações em sequência — o que, num bloco de depoimentos, é a leitura certa.
Movimento reduzido / toque
Com prefers-reduced-motion: reduce:
- não há rodízio. O relógio não liga, o
LIB.pausavelnão é chamado e o botão de pausa fica[hidden]como nasceu — botão que não comanda nada é pior que botão nenhum (precedente:hero/slideshow-cinematico); - anterior e próximo continuam, e a barra continua visível: troca pedida por alguém não é movimento imposto a ninguém;
- a expansão do painel continua acontecendo, sem os 1,1s de deslize — o painel troca de largura no quadro seguinte. Quem pediu menos movimento perde a coreografia, não a peça;
- o sinal
+/−troca de desenho sem transição, pelo mesmo motivo.
?motion=on na URL força tudo de volta, para gravar vídeo ou apresentar.
No toque (≤900px): o leque vira acordeão vertical. A barra fechada tem 96px e abre para 290px no modo serviço e 380px no depoimento — medido com os quatro relatos deste markup, o bloco de texto mais alto dá 251px a 760 de largura e 293px a 390, e 380 é o menor número que deixa os dois inteiros e ainda mostra foto. Abaixo de 560px a citação encolhe para 17px em vez de o painel crescer de novo. O rótulo deixa de ser vertical (em 96px de altura não há eixo para girar) e o sinal passa a dividir a linha com o nome, alinhado a ele com 1px de diferença. A barra de controles vira uma linha própria abaixo da ressalva.
Histórico
| Projeto | Onde | Data | Diferenças |
|---|---|---|---|
| Liberta Wealth | index.html #solucoes |
2026-08 | Origem. Classes internas sem prefixo (.veil, .n, .vert, .full), .open em vez de .is-open, sem acesso por teclado, border-radius:10px cravado. |
| referência: skinboost-design-review.vercel.app | dobra #historias — o leque de retratos com relato e rodízio |
2026-09-21 | Referência visual. Trouxe o modo depoimento (.fan-depo) e o rodízio opcional (data-fan-auto), sem tocar no modo serviço. |
Normalizações aplicadas na extração (2026-08): prefixo em todas as classes internas; .open → .is-open (evita colidir com acordeão e drawer); raio cravado → --radius-media; escurecimento passou a ler --dark-rgb; sage cru → --accent; adicionado tabindex, :focus-visible, navegação por setas e Enter/Espaço; script passou a partir de [data-fan] em vez de .fan global, para suportar múltiplos leques.
O que a tradução de 2026-09-21 mudou. A referência preenche o mesmo leque com relato de pessoa em vez de serviço, e o faz trocar sozinho. Não virou componente novo porque o motor é idêntico — duplicar o CSS para mudar conteúdo seria manter duas cópias da mesma curva. Entrou como variação declarada, e tudo o que o modo pede de diferente saiu por parafuso do próprio componente (--fan-open 3.4 → 4.6, --fan-full-w 440 → 560, --fan-say novo), sem nenhum token global novo.
- Saiu: GSAP/ScrollTrigger, que no original dirige a expansão e o rodízio — aqui a expansão continua sendo
transitiondeflexe o rodízio é umsetTimeoutde--fan-auto, sem nenhum quadro desenhado por JavaScript. Ícones Phosphor viraram SVG inline de path próprio. Fonte e fotos não atravessam (§11): os retratos são os placeholders deshowroom/demo/. - Entrou, e o original não tinha: o botão de pausa da WCAG 2.2.2, que é a primitiva
.btn-playdo core ligada porLIB.pausavel, com a copy dos dois rótulos no markup e não no script; a pausa automática em aba oculta, dobra fora da tela, ponteiro e foco, toda ela passando porctl.trocar()para o ícone nunca discordar do relógio; a distinção entre foco num painel (pausa) e foco nos botões (não pausa); e o desligamento completo do rodízio comprefers-reduced-motion. - Entrou também, e é a decisão de forma mais pesada: o compliance. A ressalva com a primitiva
.notafica colada ao leque, não no rodapé da página, e as quatro citações de exemplo foram escritas para serem inofensivas por construção. A seção Compliance desta ficha diz o que exigir antes de publicar um depoimento real. - Ganho de tabela para quem já usava:
--fan-openpassou a ser parafuso. A variação "proporção da abertura", que antes pedia edição doestilo.css, agora é uma linha no tema.
Divergência conhecida vs. produção: a Liberta Wealth rodou com border-radius:10px nos painéis, o que contraria a regra flat de canto vivo do próprio projeto. Aqui o valor vem do tema (--radius-media:0). Se for sincronizar a biblioteca de volta para o site, essa é uma mudança visual real — decida antes.