Popover

Pro

Premium anchored panel with 7 variants, 8 auto-flipping placements, an optional pointer arrow, click/hover/manual triggers, and header/subtitle/close.

</>sv-popover popoveroverlayfloatingvariantarrow
import { SvPopoverComponent } from '@styloviz-pro/overlays';

Demo & usage

Basic usage

Pro

Usage example — open the gallery for the full interactive demo.

Pro

An interactive demo for this component lives in the gallery.

Open live demo

More examples

Semantic variant with arrow

Pro

Hover with delay

Pro

Manual control

Pro

Menu behaviour

Pro

Passive callout vs interactive mini form

Pro

The Popout preset

Pro

When to use

  • Showing rich, possibly interactive content anchored to a trigger.
  • Detail cards, mini-forms or menus that exceed what a Tooltip can hold.
  • Semantic confirmations or status callouts — reach for the matching `variant`.
  • For plain hint text, a Tooltip is lighter; for a list of actions, a Dropdown Menu.

Async states

None — this component has nothing to load, so it has no loading, empty or error state to drive.

Inputs

NameTypeDefaultDescription
openboolean (two-way)falseControls popover visibility.
positionPopoverPosition'bottom-start'Placement of the popover relative to the trigger.
triggerPopoverTrigger'click'What triggers opening: click, hover, or manual (programmatic only).
titlestring''Optional header title for the popover panel.
subtitlestring''Optional secondary header line (also wired to `aria-describedby`).
variantPopoverVariant'default'Color / semantic variant.
minWidthnumber200Minimum width of the popover panel (px).
maxWidthnumber0Maximum width of the popover panel (px). 0 = unconstrained.
offsetnumber8Gap between the trigger and the panel (px).
flipbooleantrueFlip to the opposite side when there isn't room on the requested side.
showArrowbooleanfalseRender a pointer arrow that tracks the trigger.
showIconbooleantrueShow the built-in variant icon badge in the header.
showClosebooleanfalseShow a close (×) button in the header.
closeOnOutsideClickbooleantrueClose when clicking outside the trigger and panel.
closeOnEscbooleantrueClose on the ESC key.
closeOnContentClickbooleanfalseClose when any content inside the panel is clicked (menu behaviour).
hoverOpenDelaynumber0Delay before opening on hover (ms).
hoverCloseDelaynumber80Delay before closing after the pointer leaves (ms).
disabledbooleanfalsePrevent the popover from opening.
autoFocusbooleanfalseMove focus into the panel when opened via click/manual (keyboard a11y).
interactiveboolean | undefinedundefinedPassive or interactive — the axis asks for, and D2 of `docs/anchored-surfaces-adr.md`. One input moves role, focus and Tab behaviour together, and it is orthogonal to `trigger` (how it opens, never what it is). - `false` — a passive callout. The panel is `role="tooltip"` and the trigger is `aria-describedby` it while open instead of announcing a dialog popup; focus never moves in, `autoFocus` is ignored. Escape, outside click and anchor loss still dismiss it. - `true` — an interactive surface such as a mini form. `role="dialog"`, focus moves to the first focusable element (or the panel itself) on a click/manual open, and Tab / Shift+Tab cycle inside the panel instead of walking out of it. A hover open never moves focus — that is a hover card, and pulling focus to wherever the pointer drifts is not an open. - unset — exactly the behaviour before this input existed: `role="dialog"`, no Tab containment, focus moved only under `[autoFocus]`.
alignPopoverAlign | undefinedundefinedCross-axis alignment folded into `position`'s side: `position="left" align="start"` is the `left-start` placement, which `position` alone cannot spell. Unset leaves `position` exactly as written.
ariaLabelstring'Popover'Accessible label used when no `title` is provided.
customClassstring''Extra CSS classes merged onto the popover panel surface.

Outputs

EventPayload typeDescription
openedvoidEmitted after the popover opens (by any means, including `[(open)]`).
closedEventvoidEmitted after the popover closes (by any means, including `[(open)]`).
openChangebooleanEmits the new `open` whenever it changes — the output half of `[(open)]`.

Customization & theming

  • Seven variants (default · dark · primary · success · warning · danger · info) with tinted header, icon badge and accent bar.
  • Eight placements with automatic flip + viewport clamping; optional pointer arrow via `[showArrow]`. `align` (`start` · `center` · `end`) folds into the side of `position`, which adds the four side-aligned placements (`left-start` … `right-end`) — every Popout configuration is a Popover one (`SV_POPOUT_PRESET`).
  • Portaled to `document.body` and re-anchored to its trigger on scroll of any ancestor container (capture-phase) and on resize — never drifts or leaves the viewport.
  • Click / hover / manual triggers, configurable `offset`, hover delays, and min/max width. A hover popover also keeps itself open while the pointer travels across the gap towards the panel (a safe corridor, not a longer timer), so reaching for it does not dismiss it — and turning away from the panel mid-way starts the close again rather than leaving it stuck open.
  • Placement arithmetic is shared with Dropdown Menu, Popout and Tooltip: twelve placements, conditional flip, viewport clamp, `start`/`end` mirrored for RTL text, and correction for a transformed ancestor that has become the containing block.
  • If the trigger is removed or collapses to nothing while the popover is open — the row it sits in is deleted, its `<details>` closes — the popover closes rather than floating over unrelated content. The trigger is size-watched while open, so this happens at the collapse, not at the next scroll.
  • Header (title + subtitle + optional close button), content and footer slots; `closeOnOutsideClick` / `closeOnEsc` / `closeOnContentClick` toggles; `customClass` on the surface.
  • Responsive: the panel width is capped to the viewport (`max-w-[calc(100vw-1rem)]`) on top of flip + clamp, so wide content can never overflow a small screen.

Best practices

  • Do: Leave `[flip]` on (default) so the panel stays on-screen; add `[showArrow]` to anchor it visually to the trigger.
  • Do: Use `trigger="click"` (or `"manual"`) for interactive content so it doesn't vanish on mouse-out.
  • Do: Match the `variant` to intent — `danger` for destructive callouts, `success` for confirmations.
  • Do: Say which kind of popover it is with `[interactive]`: `false` for a passive callout (announced as a description of the trigger, never takes focus), `true` for anything with fields or buttons (a dialog that takes focus and keeps Tab inside). Unset keeps the original behaviour. `trigger` stays separate — it says how the panel opens, never what it is; `trigger="hover"` with `[interactive]="true"` is a hover card.
  • Tip: Set `[hoverOpenDelay]` on hover popovers to avoid opening on an accidental pass-over. `[hoverCloseDelay]` is a fallback, not the mechanism that keeps the panel reachable — the pointer travelling towards the panel already cancels the close.

Accessibility

  • Trigger exposes `aria-haspopup="dialog"`, `aria-expanded` and `aria-controls`; the panel is `role="dialog"` labelled via `aria-labelledby`/`aria-label` and described via `aria-describedby` (subtitle).
  • With `[autoFocus]` focus moves into the panel on click/manual open; on close focus returns to the trigger unless the user has already put it somewhere else (an outside click on another control keeps its focus). Closes on Esc and outside click (both configurable).
  • `[interactive]="false"` makes the panel `role="tooltip"` and references it from the trigger's `aria-describedby` while open, with no `aria-haspopup` — it never takes focus, and it renders no Close button even with `[showClose]`, because a tooltip must not hold controls (dev mode warns if you project a focusable element into one). `[interactive]="true"` keeps `role="dialog"`, moves focus to the first focusable element (or the panel, `tabindex="-1"`) on a click/manual open, and keeps Tab / Shift+Tab inside the panel.

Feedback on Popover

Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.

Loads giscus.app and GitHub — nothing is fetched until you click.