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:
- Abertura e fechamento ao clicar no botão
- Light dismiss — fecha ao clicar fora ou pressionar Esc
- Renderização no top layer — acima de qualquer
z-indexeoverflow: hidden - Gerenciamento de foco automático
- Atributos ARIA inferidos pelo browser
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:
| Valor | Comportamento |
|---|---|
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
- Tooltips e hints de formulário — o caso mais simples
- Menus de contexto e dropdowns — combinados com Anchor Positioning
- Painéis laterais (drawers) — com
popover="manual" - Notificações e toasts — múltiplos popovers manuais empilhados
Nota: para modais que bloqueiam o foco na página (focus trap), o elemento
<dialog>ainda é a escolha certa. Opopoveré 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.