# The Popover API Explained

Source: https://www.egnworks.com/blog/popover-api-explained  
Author: Jacob Val  
Published: 2026-09-16  
Updated: 2026-09-16  
Category: Frontend Architecture  
Tags: Popover API, HTML

> A deep technical guide to the native Popover API, covering the popover attribute, popovertarget, the top layer, light-dismiss behavior, the popover stack, styling with :popover-open and ::backdrop, the toggle events, and how it relates to CSS anchor positioning and the dialog element.

---

A menu, a tooltip, a notification toast, and a dropdown are all, structurally, the same problem: an element that needs to render above everything else on the page, close when the visitor clicks outside it or presses Escape, and move keyboard focus in and out correctly as it opens and closes. None of that has ever been free. Every team either reached for a component library or rebuilt the same focus trapping, outside click detection, and z-index management from scratch. The Popover API turns this into three HTML attributes.

## What Building This by Hand Required

A hand rolled popover needs a way to render above the rest of the page regardless of any ancestor's `overflow: hidden` or `z-index` stacking context, which usually meant portaling the element to a sibling of `body` in the DOM. It needs an outside click listener attached to the document and removed again on close, careful enough not to fire on the same click that opened it. It needs an Escape key listener. It needs to return focus to whatever triggered it once it closes, and it needs ARIA attributes wired up correctly so a screen reader announces it as a real popup rather than an arbitrary `div`.

Every one of those pieces is small in isolation, but doing all of them correctly, for every popover on a page, is exactly the kind of infrastructure a component library exists to provide. The Popover API provides the same infrastructure natively.

## The Three Attributes That Create a Popover

A popover element declares itself with the `popover` attribute, and a button controls it with `popovertarget` pointing at that element's `id`.

```html
<button popovertarget="user-menu">Account</button>

<div id="user-menu" popover>
  <a href="/profile">Profile</a>
  <a href="/settings">Settings</a>
</div>
```

That alone is a complete, working popover. The button toggles the menu open and closed, the menu renders on the top layer above everything else on the page, clicking outside it or pressing Escape closes it, and focus returns to the button afterward, all without a line of JavaScript.

## Auto, Manual, and Hint

The `popover` attribute takes one of three values, and each changes how the popover behaves when something else on the page happens.

| Value | Light dismiss | Closes other popovers | Typical use |
| --- | --- | --- | --- |
| `auto` (default) | Yes, outside click or Escape closes it | Opening one closes other `auto` popovers not in its ancestor chain | Menus, dropdowns, standard popovers |
| `manual` | No, only explicit code or a control closes it | No | Persistent notifications, multiple simultaneous popovers |
| `hint` | Yes | Does not close `auto` popovers, closes other `hint` popovers outside its ancestor chain | Tooltips shown on hover or focus, layered on top of an open `auto` popover |

`auto` covers the overwhelming majority of real popovers, since the behavior it describes, one thing open at a time, dismissible by clicking away, is what most menus and dropdowns already need. `manual` exists for the cases where several things need to be visible independently, such as a stack of toast notifications that should not close each other.

## Controlling the Action Directly

`popovertarget` alone toggles the popover, but `popovertargetaction` pins a button to a specific action instead.

```html
<button popovertarget="user-menu" popovertargetaction="show">Open menu</button>
<button popovertarget="user-menu" popovertargetaction="hide">Close menu</button>
```

The same target can have multiple controls, each responsible for one direction, which is useful when the close control lives inside the popover itself rather than only on the trigger that opened it.

## Styling an Open Popover

The `:popover-open` pseudo-class matches a popover element while it is showing, which is what makes an entrance or exit transition possible without JavaScript toggling a class.

```css
[popover] {
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity 0.15s, transform 0.15s, display 0.15s allow-discrete;
}

[popover]:popover-open {
  opacity: 1;
  transform: translateY(0);
}
```

A popover also gets a `::backdrop` pseudo-element covering the rest of the viewport while it is open, the same mechanism `<dialog>` uses for its modal backdrop, which is useful for dimming or blurring the page behind a more prominent popover.

```css
#user-menu::backdrop {
  background: rgb(0 0 0 / 0.25);
}
```

## Reacting to Open and Close in JavaScript

`beforetoggle` fires just before a popover's state changes and can cancel the change, while `toggle` fires after it has already happened, both carrying `oldState` and `newState` on a `ToggleEvent`.

```js
const menu = document.getElementById("user-menu");

menu.addEventListener("toggle", (event) => {
  if (event.newState === "open") {
    trackMenuOpened();
  }
});
```

The element also exposes `showPopover()`, `hidePopover()`, and `togglePopover()` directly, for the cases where opening a popover needs to happen from something other than a button click, such as a keyboard shortcut or the result of a fetch call.

## How the Popover Stack Works

Opening a second `auto` popover while one is already open does not simply stack them. Unless the new one is nested inside the first, either in the DOM or connected through `popovertarget`, opening it closes the previous one, which is what keeps a page from accumulating open menus that all need to be dismissed separately. A popover invoked from a button inside another open popover is treated as part of the same stack and does not close its parent, which is what allows a submenu to open without collapsing the menu it belongs to.

## Where Anchor Positioning Fits In

The Popover API decides whether something is open, how it is dismissed, and where it sits in the top layer, but it has no opinion on where a popover is positioned relative to the button that opened it. That is exactly what CSS anchor positioning solves, and the two are meant to be used together, an anchor name on the trigger and `position-anchor` with `anchor()` or `position-area` on the popover itself, so a menu still opens at its trigger's location once the API places it in the top layer.

## Popover Versus Dialog

Both `popover` and `<dialog>` render on the top layer, which raises the question of when to reach for which one.

| Situation | Element |
| --- | --- |
| A menu, dropdown, or tooltip the rest of the page stays interactive during | `popover` |
| A form or confirmation that should block interaction with the rest of the page until resolved | `<dialog>` opened with `showModal()` |
| A non-modal notification that should not steal focus | `popover="manual"` |

A `<dialog>` opened with `showModal()` is genuinely modal, it traps focus and disables interaction with everything behind it, which a popover never does regardless of which of the three values it uses.

## Browser Support

The Popover API is supported in Chrome, Edge, Firefox, and Safari, and reached Baseline availability in 2024, making it broadly usable today without a fallback path for a project targeting current browser versions.

## Conclusion

The Popover API takes the part of building a popover that was never actually specific to any one design, top layer rendering, light-dismiss, focus handling, and Escape key support, and makes it declarative HTML rather than application code repeated in every project that needs it. `popover` and `popovertarget` cover the open and close behavior, `:popover-open` and `::backdrop` cover the styling, and CSS anchor positioning covers the one piece the API deliberately leaves out, exactly where the popover should sit.

## References

[MDN: Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API)

[MDN: popover Global Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/popover)

[Chrome for Developers: Introducing the Popover API](https://developer.chrome.com/blog/introducing-popover-api)

[Can I Use: Popover API](https://caniuse.com/mdn-api_htmlelement_popover)
