Skip to content
The modern web platformLesson 5 of 7Intermediate13 min

The Popover API

The native popover attribute gives any element show/hide, light-dismiss, Esc, and the top layer for free — menus and tooltips with almost no JavaScript.

Kept on this device only.

In one line: add popover to an element and popovertarget to a button, and the browser hands you a top-layer overlay with show/hide, click-outside dismiss, and Esc — no JavaScript state machine required.

What it is

A popover is any transient overlay that sits above the page: a menu, a tooltip, a date picker, a small dialog. For years these meant a pile of JavaScript — tracking open state, wiring an outside-click listener, handling Esc, juggling z-index, and trapping focus.

The Popover API moves all of that into the platform. Mark an element with the popover attribute and point an invoker at it with popovertarget, and the browser gives you: a toggle, light-dismiss (clicking outside closes it), Esc to close, and rendering in the top layer so it escapes overflow: hidden and z-index entirely.

Why it matters

It deletes the most bug-prone code in a typical component library. Light-dismiss, Esc handling, and top-layer stacking are exactly the things hand-rolled overlays get subtly wrong — a listener that leaks, an Esc that closes the wrong layer, a dropdown clipped by an ancestor's overflow.

It also pairs with two siblings to complete the overlay story: anchor positioning decides where the popover sits relative to its trigger, and @starting-style lets the entry animate from a real CSS starting state — so you get a tasteful fade/zoom-in without a JS animation library.

See it

Live demo
Tweak it2
Open the native popover; click outside to light-dismiss. Switch between a menu and a tooltip.

Open the popover and click outside it — that dismiss is the browser's, not ours. Switch between an action menu and a tooltip, and toggle the entry animation.

How it works

Three pieces:

  • popover on the overlay element. By default it is popover="auto": opening one auto-popover closes others, and it supports light-dismiss + Esc. Use popover="manual" when you want to control dismissal yourself.
  • popovertarget on a <button> makes it an invoker that toggles the popover by id — zero JavaScript for the common case. (popovertargetaction can force "show" or "hide".)
  • The top layer. An open popover renders above all page content, so z-index wars and clipping ancestors stop mattering.

To animate, transition the popover's opacity/transform and declare the entry starting point inside @starting-style — the browser interpolates from there to the open state. Wrap it in a reduced-motion guard.

Build it

HTML — a button invokes a popover by id, no JS
<button popovertarget="menu" aria-haspopup="menu">Actions ▾</button>
 
<div id="menu" popover role="menu">
  <button role="menuitem">Rename</button>
  <button role="menuitem">Duplicate</button>
  <button role="menuitem">Delete</button>
</div>

Make it yours

Use the controls beside the demo above to change popover contents and animate entry — each change updates the example live.

Experiment in the playground
  • Open the menu, then click the empty stage around it — light-dismiss closes it with no listener of yours.
  • Switch to the tooltip variant: same machinery, different role and contents.
  • Toggle the animation off to feel the reduced-motion path — the popover still opens, just without the transition.

Reproduce it with an LLM

Reproduce it with an LLM

You are a senior front-end engineer. Build an actions menu using the native Popover API: a trigger `<button popovertarget="menu">` and a `<div id="menu" popover>` containing menu items, so the browser handles show/hide, light-dismiss (click outside), and Esc with no JavaScript. Render it in the top layer and animate the entry with a `transition` plus `@starting-style` for opacity/transform, fully disabled under `prefers-reduced-motion: reduce`. Give the trigger `aria-haspopup` and the menu the right roles, keep keyboard focus order sensible, and provide a small JS enhancement only where the platform can't (e.g. arrow-key roving among items). Return only the HTML, CSS, and minimal JS.

Pitfalls & accessibility

Further reading