HTML JavaScript

Popover API: modais e tooltips nativos sem JavaScript

O atributo HTML que substitui bibliotecas inteiras de modal e tooltip. Acessibilidade, foco e dismiss automáticos — sem escrever uma linha de JS.

· 6 min de leitura

Quantas vezes você já instalou uma biblioteca só para ter um modal acessível? Headless UI, Radix, Floating UI — todas ótimas, mas todas resolvendo um problema que o browser agora resolve nativamente.

A Popover API entrou no baseline em 2024 e tem suporte em todos os browsers modernos. O resultado: um sistema completo de sobreposição de conteúdo com um único atributo HTML.

O mínimo que funciona

<button popovertarget="meu-popover">Abrir</button>

<div id="meu-popover" popover>
  Conteúdo do popover aqui.
</div>

Só isso. Sem JavaScript, sem CSS extra. O browser já entrega:

Top layer: o fim dos problemas de z-index

Este é o detalhe mais importante. Elementos com popover são renderizados no top layer — uma camada especial do browser que fica acima de todo o DOM normal, independente de z-index, overflow ou transform nos ancestrais.

Acabou o problema clássico de "meu modal some porque o container pai tem overflow: hidden". O popover escapa do stacking context.

Tipos de popover

O atributo popover aceita dois valores:

ValorComportamento
popover="auto" (padrão) Fecha ao clicar fora (light dismiss). Só um aberto por vez no mesmo grupo.
popover="manual" Não fecha automaticamente. Controle total via JS ou botão dedicado.

Controle via JavaScript

Quando precisar de controle programático, três métodos novos no elemento:

const pop = document.getElementById('meu-popover');

pop.showPopover();   // abre
pop.hidePopover();   // fecha
pop.togglePopover(); // alterna

E dois eventos para reagir às mudanças:

pop.addEventListener('beforetoggle', (e) => {
  console.log(e.oldState); // "closed" ou "open"
  console.log(e.newState); // "open" ou "closed"
});

pop.addEventListener('toggle', (e) => {
  // popover já mudou de estado
});

Estilizando com CSS

O estado aberto/fechado é acessível via pseudo-classe:

[popover] {
  /* estilo base — mesmo quando fechado */
  background: #1a1a1a;
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 12px;
  padding: 1.5rem;
  color: white;

  /* reset do estilo padrão do browser */
  border: none;
  margin: auto;
}

[popover]:popover-open {
  /* aplicado apenas quando aberto */
}

/* Backdrop nativo */
[popover]::backdrop {
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(4px);
}

Animando a entrada e saída

Por padrão, o popover aparece e desaparece sem animação. Para animar, combine com @starting-style (tema do próximo artigo) ou use transições simples:

[popover] {
  opacity: 0;
  transform: scale(.95);
  transition: opacity .2s, transform .2s,
              display .2s allow-discrete,
              overlay .2s allow-discrete;
}

[popover]:popover-open {
  opacity: 1;
  transform: scale(1);
}

@starting-style {
  [popover]:popover-open {
    opacity: 0;
    transform: scale(.95);
  }
}

A keyword allow-discrete é necessária porque display e overlay são propriedades discretas — não interpolam valores intermediários, então o browser precisa de permissão explícita para incluí-las na transição.

Casos de uso ideais

Nota: para modais que bloqueiam o foco na página (focus trap), o elemento <dialog> ainda é a escolha certa. O popover é para conteúdo que coexiste com a página sem travar o fluxo do usuário.

Suporte

Baseline amplamente disponível desde 2024. Chrome, Edge, Firefox e Safari com suporte completo. Sem necessidade de polyfill para projetos novos.


A Popover API não substitui todos os casos de uso de bibliotecas de componentes, mas elimina o JavaScript boilerplate para o caso mais comum: mostrar e esconder um pedaço de conteúdo. Menos dependência, menos bundle, mesma experiência.