Acordeão, popover e carrossel sem plugin: o que o HTML e o CSS já fazem
Troque plugins de abrir e fechar por details, popover, dialog e scroll-snap, com teclado e leitor de tela funcionando, e saiba em que ponto o script volta a ser necessário.
A página de produto da loja carrega jQuery e mais quatro plugins: um para o acordeão de perguntas frequentes, um para o menu "Mais opções", um para a janela de tabela de medidas e um para o carrossel de fotos. Quando a CDN de um deles demorou, as perguntas ficaram todas fechadas e sem clique, e a tabela de medidas sumiu. Boa parte dessas peças o navegador já entrega pronta, com teclado e leitor de tela funcionando, sem uma linha de JavaScript.
A resposta curta: acordeão é details e summary; menu e balão que abrem por clique são o atributo popover; janela modal é dialog; carrossel simples é rolagem com scroll-snap; barra de progresso é progress. Você estiliza com CSS. O script entra quando o comportamento vai além do que o nativo cobre, e aí ele herda a responsabilidade pelo teclado.
Por que o plugin sai mais caro do que parece
Cada plugin é um arquivo a baixar, interpretar e executar antes de a peça funcionar. Se ele falha, a informação que estava dentro do componente fica presa: a resposta da pergunta frequente existe no HTML, mas escondida por um display: none que só o script remove. A falha de um arquivo vira falha de conteúdo.
Há um custo menos visível. Quem reimplementa um acordeão com div e clique precisa recriar o que o elemento nativo já fazia: receber foco com Tab, abrir com Enter e espaço, anunciar ao leitor de tela se está aberto ou fechado. Na prática, quase todo plugin caseiro erra pelo menos um desses casos. O navegador acerta todos, porque foi testado por muito mais gente.
Como trocar cada plugin pelo elemento nativo
- Liste as peças interativas da página. Para cada uma, anote o que ela faz: abre e fecha, aparece por cima, bloqueia o resto da página, rola horizontalmente.
- Associe cada comportamento ao elemento nativo. Abre e fecha no fluxo:
details. Aparece por cima e fecha ao clicar fora:popover. Bloqueia a página até responder:dialogmodal. Rola entre itens:scroll-snap. - Troque uma peça por vez e teste com teclado. Tab até o controle, Enter ou espaço para abrir, Esc para fechar onde fizer sentido.
- Estilize sem esconder o foco. Use
:focus-visiblepara um contorno claro em quem navega por teclado. - Remova o plugin e a dependência. Só depois de a peça nativa passar no teste. Se nada mais usa o jQuery, ele sai também.
Acordeão com details e summary
<details name="faq">
<summary>Qual o prazo de troca?</summary>
<p>Você tem 30 dias a partir do recebimento.</p>
</details>
<details name="faq">
<summary>O frete da troca é grátis?</summary>
<p>Sim, na primeira troca de cada pedido.</p>
</details>
O summary já é focável e responde a Enter e espaço. O atributo name igual em vários details faz deles um grupo exclusivo: abrir um fecha o outro. Esse atributo é mais recente; num navegador antigo, os blocos só deixam de ser exclusivos e continuam funcionando. Se o JavaScript falhar, nada muda, porque não havia JavaScript. E, em parte dos navegadores, a busca da página com Ctrl+F já encontra e abre o texto de um details fechado, o que um display: none de plugin nunca permite.
Menu e balão com popover
<button popovertarget="mais-opcoes">Mais opções</button>
<div id="mais-opcoes" popover>
<a href="/lista-de-desejos">Salvar na lista</a>
<a href="/comparar">Comparar</a>
</div>
O atributo popover é suportado pelos navegadores principais desde 2024. O botão abre e fecha o painel sem script, o painel aparece na camada superior da página (sem briga de z-index), fecha com Esc e com clique fora. Posicionar o painel colado ao botão pede CSS; o posicionamento por âncora existe, mas o suporte ainda varia, então confira antes de depender dele.
Janela modal com dialog
<button type="button" id="abrir-medidas">Tabela de medidas</button>
<dialog id="medidas">
<h2>Tabela de medidas</h2>
<p>P: 88 cm de busto. M: 96 cm. G: 104 cm.</p>
<form method="dialog">
<button>Fechar</button>
</form>
</dialog>
<script>
document.getElementById('abrir-medidas').addEventListener('click', function () {
document.getElementById('medidas').showModal();
});
</script>
Aqui sobra uma linha de script para abrir em modo modal, e ela vale a pena. O showModal() prende o foco dentro da janela, deixa o resto da página inerte, fecha com Esc e devolve o foco ao botão. O form method="dialog" fecha sem script. Fazer isso à mão é exatamente o tipo de código em que plugin caseiro erra.
Carrossel com scroll-snap
.fotos {
display: flex;
gap: 1rem;
overflow-x: auto;
scroll-snap-type: x mandatory;
}
.fotos img {
flex: 0 0 85%;
scroll-snap-align: start;
}
A rolagem é a do sistema: dedo no celular, trackpad no notebook, setas do teclado quando a área tem foco. Cada foto "encaixa" no início. O que não vem pronto são os pontinhos e as setas de navegação. Se forem indispensáveis, eles são o pedaço de script, e não o carrossel inteiro.
Onde o script continua necessário
Abas são o exemplo clássico. O truque de rádio escondido com CSS funciona visualmente, mas o leitor de tela anuncia botões de rádio, não abas, e a navegação por setas não segue o padrão que a pessoa espera. Para abas de verdade, você precisa de papéis ARIA e de script de teclado. Muitas vezes a saída mais simples é não fazer abas: títulos com details ou uma página por seção resolvem.
Também precisam de script a validação que depende do servidor, o autocompletar com busca remota e qualquer estado que precise persistir entre páginas. O critério é este: o script entra quando o nativo não alcança, não por hábito. E ao entrar, ele assume o que o nativo fazia. Para os cuidados com nome e foco em componentes feitos só com CSS, veja componente CSS ainda precisa de nome e foco.
Erros comuns
- Colocar o conteúdo dentro de um elemento que só o script mostra. Se o arquivo não carrega, a resposta da pergunta frequente simplesmente não existe para a pessoa.
- Trocar
buttonpordivcom clique. A peça deixa de receber foco por Tab e de responder a Enter, e o leitor de tela não a anuncia como botão. - Remover o contorno de foco no CSS. Quem navega por teclado perde o lugar onde está na página.
- Usar o truque do checkbox para modal. Não prende o foco nem deixa o fundo inerte, e a pessoa tabula para fora da janela sem perceber.
- Esconder a rolagem do carrossel. Sem barra e sem setas, quem usa mouse sem rolagem horizontal não chega à segunda foto.
- Somar plugins para cada peça. O peso cresce e os conflitos aparecem; o guia sobre juntar todos os scripts do kit mostra o efeito.
Checklist
- Cada peça interativa da página foi associada a um elemento nativo ou justificada como script.
- O acordeão usa
detailsesummary. - Menus que abrem por cima usam
popoverou têm teclado e Esc testados. - A janela modal usa
dialogcomshowModal(). - Toda a navegação foi testada só com teclado.
- O contorno de foco aparece com
:focus-visible. - Com o JavaScript desligado, nenhum conteúdo de texto fica inacessível.
Campos de formulário seguem a mesma lógica; os inputs HTML5 que trabalham antes do JavaScript são o próximo passo. Agora abra a sua página de produto, desligue o JavaScript nas ferramentas do navegador e recarregue: alguma informação que estava lá antes desapareceu?
Referência: http://tableless.com.br/elementos-de-interface-utilizando-apenas-css3/