Programação Por Equipe DevBras

Capa do artigo: Acordeão, popover e carrossel sem plugin: o que o HTML e o CSS já fazem

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

  1. 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.
  2. 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: dialog modal. Rola entre itens: scroll-snap.
  3. 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.
  4. Estilize sem esconder o foco. Use :focus-visible para um contorno claro em quem navega por teclado.
  5. 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 button por div com 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 details e summary.
  • Menus que abrem por cima usam popover ou têm teclado e Esc testados.
  • A janela modal usa dialog com showModal().
  • 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?

Avalie este artigo

Referência: http://tableless.com.br/elementos-de-interface-utilizando-apenas-css3/