HTML moderno sem frameworks: dialog, details e Popover API na prática

Aprenda a criar modais, áreas expansíveis e popovers com recursos nativos do HTML, menos JavaScript e uma base acessível.

Compartilhe

Interface HTML moderna com modal, popover e painel expansível em tons de azul e ciano

HTML moderno: estrutura, semântica e acessibilidade

Estrutura semântica de uma página HTML moderna

HTML moderno: fundamentos e como a Web é estruturada

Editor e navegador conectados por um documento HTML

Seu primeiro documento HTML: do arquivo ao navegador

Árvore de elementos e sintaxe HTML

Elementos e sintaxe HTML: tags, conteúdo e aninhamento

Controles e propriedades de atributos HTML

Atributos HTML: propriedades, valores e boas práticas

Hierarquia visual de cabeçalhos HTML

Cabeçalhos HTML: hierarquia clara para pessoas e buscadores

Blocos estruturados de parágrafos e conteúdo pré-formatado

Parágrafos HTML: texto, quebras e conteúdo pré-formatado

Navegação entre páginas por links e âncoras

Links HTML: navegação acessível, URLs e segurança

Elementos semânticos de texto HTML

Semântica de texto HTML: ênfase, citações e código

Camadas de estrutura HTML e apresentação CSS

CSS no HTML: como conectar estrutura e apresentação

Imagem adaptada a telas de diferentes tamanhos

Imagens responsivas em HTML: desempenho e acessibilidade

Tabela de dados HTML com cabeçalhos acessíveis

Tabelas HTML acessíveis: estrutura para dados

Listas ordenadas, não ordenadas e de descrição

Listas HTML: ul, ol, dl e estruturas aninhadas

Formulário HTML acessível com campos e controles

Formulários HTML acessíveis: campos, rótulos e validação

Conteúdo externo protegido em um iframe

Iframes em HTML: incorporação segura e acessível

Atributos globais aplicados a um elemento HTML

Atributos globais HTML: idioma, dados, foco e identidade

Biblioteca organizada de elementos HTML modernos

Referência de elementos HTML: guia atual e semântico

Interface HTML moderna com modal, popover e painel expansível em tons de azul e ciano

HTML moderno sem frameworks: dialog, details e Popover API na prática

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 com show() ou modal com showModal().
  • 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 details somente para conteúdo complementar no fluxo.
  • Dê ao dialog um 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.

HTML moderno: estrutura, semântica e acessibilidade

Referência de elementos HTML: guia atual e semântico

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.