← Overview

popover

Anchor-positioned popover on the native Popover API — behaviour and placement without JavaScript.

All behaviour is native. Every trigger below is a plain <button popovertarget="…"> — the browser toggles the popover, renders it in the top layer and (for the default popover="auto") closes it on an outside click or Esc. The placement next to the trigger is pure CSS: CSS anchor positioning, explained below the examples.

✔ This browser supports CSS anchor positioning — the popovers on this page are placed by the stylesheet alone; the component's JavaScript does nothing but validate the markup.

✖ This browser does not support CSS anchor positioning yet — the component's JavaScript fallback is anchoring the popovers instead (measured per frame, same sides, same flip behaviour).

Default (light dismiss, placed below)

A default popover="auto": click outside it or press Esc and it closes itself — no close button required. It sits below its trigger because bottom is the default placement.

Placement (data-popover-position)

position-area: top — centered above the trigger.

position-area: right — centered right of the trigger.

position-area: bottom — the default.

position-area: left — centered left of the trigger.

Anchored, not parked — scroll while it is open

The popover is tethered to its anchor, not to a coordinate: open it, then scroll the page. It follows the trigger, and when the space below runs out it flips above it (position-try-fallbacks: flip-block) — the browser re-evaluates the placement continuously.

Keep scrolling — I stay glued to my trigger and flip to its other side at the viewport edge.

Manual (popover="manual")

A manual popover has no light dismiss — outside clicks and Esc are ignored, so it must keep a visible close control. The × button closes it natively via popovertargetaction="hide"; several manual popovers can even be open at once.

Pinned note

I stay open while you click around the page — only the × (or my trigger) closes me.

CSS anchor positioning in a nutshell

A top-layer overlay is a position: fixed box that knows nothing about its trigger — placing it next to one used to mean JavaScript (or a library like Floating UI). Anchor positioning (Chrome/Edge 125+, Safari 26+, Firefox in development) moves that into CSS:

.trigger { anchor-name: --my-anchor; }

.overlay {
  position: fixed;
  position-anchor: --my-anchor;
  inset: auto;           /* undo the UA centering of [popover] */
  position-area: bottom; /* below the anchor, centered         */
  position-try-fallbacks: flip-block;
}
  • Implicit anchor: this component ships no anchor-name at all — a popover opened via popovertarget automatically gets that button as its anchor.
  • position-area places the overlay on a 3×3 grid around the anchor (top, bottom span-right, block-end, …); naming only one axis centers it on the other. For fine-grained control there are also top: anchor(bottom) and anchor-size().
  • position-try-fallbacks keeps the overlay on screen: the placement is re-evaluated on every scroll and resize, flipping to the opposite side when space runs out — see the “scroll while it is open” example above.
  • No support? Detected with the same @supports condition in CSS and JS: popover.js then measures the trigger itself and feeds --popover-fallback-* custom properties to the stylesheet; without JS the UA default (centered in the viewport) still works.