HTML moderno: estrutura, semântica e acessibilidade
Durante muito tempo, abrir um modal, criar um menu flutuante ou montar uma seção expansível significava instalar uma biblioteca ou escrever uma quantidade considerável de JavaScript. O HTML moderno mudou esse cenário. Elementos como <details> e <dialog>, somados à Popover API, entregam comportamento nativo, semântica e recursos importantes de acessibilidade diretamente no navegador.
Isso não significa que todo componente complexo deixou de precisar de JavaScript. Significa que agora podemos começar de uma base mais robusta e adicionar código somente onde existe uma necessidade real. Neste tutorial, você vai criar três componentes práticos e entender quando usar cada um.
Escolha o elemento pelo comportamento, não pela aparência
Os três recursos podem revelar conteúdo, mas resolvem problemas diferentes. A escolha correta começa pela interação esperada:
<details>: revela informação complementar dentro do fluxo da página. É ideal para perguntas frequentes, notas e explicações opcionais.<dialog>: cria uma janela que exige atenção ou uma decisão. Pode ser não modal comshow()ou modal comshowModal().- Popover API: exibe uma camada leve e temporária relacionada a um acionador, como um menu, uma ajuda contextual ou um seletor.
Não use <details> para simular abas ou menus complexos. Também evite transformar qualquer caixa flutuante em modal: se a pessoa precisa continuar interagindo com a página, um popover provavelmente é mais adequado.
Details e summary: expansão sem JavaScript
O par <details> e <summary> é a solução mais direta. O navegador gerencia o estado aberto ou fechado, a interação por teclado e o atributo booleano open.
<details>
<summary>O que é HTML semântico?</summary>
<p>
É o uso de elementos que descrevem o papel do conteúdo,
facilitando a leitura por navegadores e tecnologias assistivas.
</p>
</details>
Se a informação deve começar visível, adicione open ao elemento details. Para criar um grupo no qual apenas uma seção permanece aberta, o HTML Living Standard também define o atributo name:
<details name="faq" open>
<summary>Preciso instalar alguma biblioteca?</summary>
<p>Não. O comportamento é nativo do navegador.</p>
</details>
<details name="faq">
<summary>Posso personalizar com CSS?</summary>
<p>Sim, mantendo foco visível e contraste suficiente.</p>
</details>
O conteúdo continua no fluxo do documento, ao contrário de um modal ou popover. Essa diferença torna details uma escolha especialmente boa para documentação e tutoriais.
Dialog: um modal nativo com controle de foco
O elemento <dialog> representa uma janela de diálogo. Quando aberto com showModal(), ele entra na camada superior do navegador, deixa o restante da página inerte e oferece comportamentos que seriam trabalhosos em uma implementação manual.
<button type="button" id="abrir-dialogo">
Excluir projeto
</button>
<dialog id="confirmacao" aria-labelledby="titulo-dialogo">
<h2 id="titulo-dialogo">Excluir este projeto?</h2>
<p>Esta ação não poderá ser desfeita.</p>
<form method="dialog">
<button value="cancelar" autofocus>Cancelar</button>
<button value="excluir">Excluir</button>
</form>
</dialog>
<script>
const abrir = document.querySelector('#abrir-dialogo');
const dialogo = document.querySelector('#confirmacao');
abrir.addEventListener('click', () => dialogo.showModal());
dialogo.addEventListener('close', () => {
if (dialogo.returnValue === 'excluir') {
console.log('Exclusão confirmada');
}
});
</script>
O formulário com method="dialog" fecha o componente e disponibiliza o valor do botão em returnValue. Como a operação do exemplo é destrutiva, o foco inicial foi colocado em “Cancelar”. O diálogo também precisa de um nome acessível; aqui ele vem de aria-labelledby, apontando para o título visível.
Use show() somente quando o comportamento for realmente não modal. Para confirmações, autenticação ou tarefas que interrompem o fluxo, showModal() expressa melhor a intenção.
Popover API: camadas leves declaradas no HTML
A Popover API permite associar um botão a um elemento flutuante por meio de popovertarget. No modo padrão, equivalente a popover="auto", clicar fora ou pressionar Esc fecha o popover. Esse comportamento é conhecido como light dismiss.
<button type="button" popovertarget="menu-perfil">
Abrir perfil
</button>
<nav id="menu-perfil" popover aria-label="Opções do perfil">
<a href="/perfil/">Meu perfil</a>
<a href="/configuracoes/">Configurações</a>
<button
type="button"
popovertarget="menu-perfil"
popovertargetaction="hide">
Fechar
</button>
</nav>
O atributo popovertargetaction aceita toggle, show ou hide. O valor padrão é toggle. A especificação recomenda manter o popover imediatamente depois do acionador no DOM quando isso for possível, pois a ordem de leitura fica mais previsível para tecnologias assistivas.
Existem três estados importantes: auto, que fecha outros popovers automáticos; manual, que exige controle explícito; e hint, pensado para dicas temporárias. Para a maioria dos menus contextuais, auto é o ponto de partida correto.
Quando a lógica da aplicação precisar controlar o componente, o JavaScript continua disponível com os métodos showPopover(), hidePopover() e togglePopover(). O evento toggle permite reagir à abertura e ao fechamento sem observar classes ou atributos manualmente. Ainda assim, prefira os atributos declarativos quando um botão comum resolve o caso: eles deixam a relação entre acionador e conteúdo visível no próprio HTML. Use a API JavaScript para integrar estados externos, carregar dados sob demanda ou coordenar etapas de uma interface, não apenas para reproduzir um clique que o navegador já sabe tratar.
CSS: personalize sem remover sinais de interação
O estilo visual pode seguir o seu projeto, mas foco, contraste e estados precisam continuar perceptíveis. O pseudoelemento ::backdrop personaliza o fundo de um diálogo modal.
dialog,
[popover] {
color: #eaf6ff;
background: #0f1b2d;
border: 1px solid #1cc8f2;
border-radius: 1rem;
box-shadow: 0 1.5rem 4rem rgb(0 0 0 / 45%);
}
dialog::backdrop {
background: rgb(4 10 20 / 72%);
backdrop-filter: blur(4px);
}
button:focus-visible,
a:focus-visible,
summary:focus-visible {
outline: 3px solid #55dcff;
outline-offset: 3px;
}
Não remova o outline sem oferecer um substituto evidente. Teste com zoom, teclado e modo de alto contraste. Uma interface bonita que esconde o foco deixa parte do público sem saber onde está.
Checklist prático antes de publicar
- Use
detailssomente para conteúdo complementar no fluxo. - Dê ao
dialogum título visível e um nome acessível. - Em ações destrutivas, coloque o foco inicial na alternativa segura.
- Inclua um botão claro para fechar modais e popovers.
- Mantenha o acionador próximo do popover na ordem do DOM.
- Verifique a navegação com Tab, Shift + Tab e Esc.
- Preserve contraste, foco visível e alvos de toque confortáveis.
- Teste em navegadores atuais e ofereça uma experiência básica quando um recurso não estiver disponível.
Conclusão: menos código, mais responsabilidade
Recursos nativos reduzem dependências e entregam comportamentos consistentes, mas não eliminam a necessidade de pensar na experiência. A semântica correta, a ordem do DOM, o gerenciamento de foco e os testes por teclado continuam sendo responsabilidade de quem desenvolve.
Comece substituindo um componente simples do seu projeto: uma FAQ por details, uma confirmação por dialog ou um menu contextual pela Popover API. Depois compare a quantidade de código e a experiência por teclado. Para revisar a base antes de avançar, consulte os artigos sobre elementos e sintaxe HTML, atributos HTML e formulários acessíveis. A trilha completa está na série HTML moderno.
Fontes técnicas: HTML Living Standard — Popover, HTML Living Standard — elementos interativos e W3C WAI — diálogos modais com HTML.