Skip to main content
◇ Zone 2

Popover & Dialog

Native overlays. No library required.

A real Popover

No JavaScript needed. Just popover and popovertarget. Click outside to dismiss — that's popover="auto" at work.

A native <dialog>

This is a genuine <dialog> element. It traps focus, blocks background interaction, and has a ::backdrop pseudo-element. Powered by the HTML spec — zero dependencies.

The closedby="any" attribute lets you close it by clicking the backdrop.

← Back to Observatory

The Popover API

For years, overlays required hefty JavaScript libraries — managing z-index, focus trapping, outside-click detection, Escape key handling, accessibility roles. The modern web ships all of this natively.

The Popover API and <dialog> element together give you a complete overlay system. The browser handles the top-layer promotion, focus management, light-dismiss, and backdrop — leaving you to write only the CSS you actually care about.

Browser Support ● Chrome 114+ ● Firefox 125+ ● Safari 17+ ● Edge 114+

popover attribute

Add popover to any element. The browser promotes it to the top layer — above all other content, including fixed elements and stacking contexts.

popovertarget

Wire a button to any popover by ID. The browser handles the click, the toggle, the Escape key, and the light-dismiss — no addEventListener required.

<dialog> element

A semantic, accessible modal container. Provides focus trapping, a ::backdrop layer, and integrates with the top-layer stack beside popovers.

@starting-style

Define the before-open state of an element. The browser interpolates from it on entry — enabling pure-CSS show/hide transitions that were previously impossible.

Basic Popover
<!-- The trigger button -->
<button popovertarget="my-popover">
  Show Popover
</button>

<!-- The popover itself (auto = light-dismiss + Escape) -->
<div popover="auto" id="my-popover">
  <p>I live in the top layer.</p>
</div>

<!-- manual = stays open until explicitly closed -->
<div popover="manual" id="sticky-tip">
  I won't auto-dismiss.
</div>
Dialog with closedby
<!-- closedby="any" allows backdrop-click to close -->
<dialog id="modal" closedby="any">
  <h2>Confirm Action</h2>
  <p>Are you sure?</p>
  <form method="dialog">
    <button value="confirm">Confirm</button>
    <button value="cancel">Cancel</button>
  </form>
</dialog>

<!-- Open it with JS -->
document.querySelector('#modal').showModal();

<!-- Or non-modal (no backdrop, no focus trap) -->
document.querySelector('#modal').show();
@starting-style Entry Animation
/* The final open state */
[popover]:popover-open {
  opacity: 1;
  transform: translateY(0);
  transition:
    opacity 400ms ease,
    transform 400ms ease,
    display 400ms allow-discrete,
    overlay 400ms allow-discrete;
}

/* Where the animation begins — the "before open" state */
@starting-style {
  [popover]:popover-open {
    opacity: 0;
    transform: translateY(1rem);
  }
}

/* Exit: when NOT :popover-open (via allow-discrete) */
[popover]:not(:popover-open) {
  opacity: 0;
  transform: translateY(0.5rem);
}

popovertargetaction values

toggle Default. Show if hidden, hide if visible.
show Only show the popover. Idempotent if already open.
hide Only hide the popover. Safe to call when closed.

closedby attribute

any Close on Escape, close request, or clicking outside.
closerequest Close only on Escape or platform close gesture.
none Never auto-close. Requires explicit JS or button.

Build a Popover

Configure and preview a live popover with generated code.

Popover Type
Text shown inside the popover
Generated Code