HyPopover
Usage examples on this page are written for Lit / plain HTML (
<hy-popover>). The same component ships asHyPopoverin@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
A non-modal overlay for contextual content, built on the Native Popover API (using popover="manual" mode) with Floating UI for automatic placement. Light dismiss (click outside, Escape key) is handled by the component itself for reliable cross-browser behavior.
Trigger Patterns
Slotted trigger (preferred): The trigger element is passed via the trigger slot, ensuring ARIA relationships work correctly across shadow DOM boundaries. The trigger automatically toggles the popover on click.
for attribute (fallback): When the trigger needs to be outside the popover element (e.g., in SSR frameworks), use the for attribute to reference a trigger element by ID. You'll need to add your own click handler to toggle the popover.
Positioning Technology
This component uses Floating UI for positioning instead of CSS Anchor Positioning. See the "Why Floating UI?" section in the component documentation for details on this architectural decision.
Popover vs Dialog
A popover is NOT a dialog. Use hy-popover for lightweight, non-modal, contextual overlays like tooltips, menus, and dropdowns. Use hy-dialog for modal interactions that require user attention and response.
Semantic Role
Unlike hy-dialog, popovers have no default ARIA role. Set the role attribute based on what the popover contains:
role="menu"- Action menus, context menusrole="listbox"- Selection dropdownsrole="tooltip"- Tooltips and toggletipsrole="dialog"- Dialog-like content (preferhy-dialogfor true dialogs)- No role - Let content provide its own semantics (e.g., when wrapping a component that already has proper ARIA)
Accessibility Features
- Full keyboard navigation (Escape to close)
- Light dismiss (click outside to close)
- ARIA live regions for screen reader announcements (opt-in)
- Automatic ARIA relationships with trigger element:
- For tooltips:
aria-describedby(tooltip describes the trigger) - For other roles:
aria-haspopupandaria-controls
- For tooltips:
- Role-aware focus management:
- Tooltips: Focus stays on trigger (tooltips are supplementary)
- Other roles: Focus moves to popover on open, returns on close
- The public
focusSettledpromise resolves once the open's initial-focus sequence has settled (deterministic await for tests)
- High contrast mode support
- Reduced motion support
Examples
Basic Usage (Slotted Trigger - Preferred)
<hy-popover>
<button slot="trigger">Open</button>
<p>Popover content</p>
</hy-popover>Fallback Usage (for attribute)
<button id="trigger">Open</button>
<hy-popover id="my-popover" for="trigger">
<p>Popover content</p>
</hy-popover>
<script>
const trigger = document.getElementById('trigger');
const popover = document.getElementById('my-popover');
trigger.addEventListener('click', () => popover.toggle());
</script>Placement Options
<hy-popover placement="top">
<button slot="trigger">Open</button>
Top positioned content
</hy-popover>Without Arrow
<hy-popover no-arrow>
<button slot="trigger">Open</button>
<p>No arrow indicator</p>
</hy-popover>Menu Popover
<hy-popover role="menu" label="User actions">
<button slot="trigger">Menu</button>
<button role="menuitem">Edit Profile</button>
<button role="menuitem">Settings</button>
<button role="menuitem">Logout</button>
</hy-popover>Tooltip Popover
<hy-popover role="tooltip" no-arrow>
<span slot="trigger" tabindex="0">?</span>
Press Enter to submit the form
</hy-popover>Preventing Close
<hy-popover id="popover">
<button slot="trigger">Open</button>
Content
</hy-popover>
<script>
document.getElementById('popover').addEventListener('request-hide', (e) => {
if (hasUnsavedChanges) {
e.preventDefault();
}
});
</script>API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
open | open | boolean | false | Controls the open state of the popover. |
for | for | string | '' | The id of the element that anchors this popover. Use this as a fallback when the slotted trigger pattern isn't available (e.g., when the trigger needs to be outside the popover element). |
placement | placement | 'top' | 'top-start' | 'top-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end' | 'right' | 'right-start' | 'right-end' | 'bottom' | Where to place the popover relative to the anchor. Uses Floating UI with automatic fallback (flip, shift) when there isn't enough space. |
distance | distance | number | 8 | Distance between the popover and its anchor element in pixels. This creates a gap between the anchor and the popover. |
skidding | skidding | number | 0 | Offset along the anchor's edge in pixels. Positive values move the popover in the direction of the text flow. |
noArrow | no-arrow | boolean | false | Hides the arrow indicator when true. |
label | label | string | '' | Accessible label for the popover. Applied as aria-label on the popover container for screen readers. |
popoverId | popover-id | string | '' | Auto-generated unique identifier for the popover element. Used to establish ARIA relationships between trigger and popover. |
announceStateChanges | announce-state-changes | boolean | false | Whether to announce state changes to screen readers via ARIA live regions. When enabled, announces "opened" and "closed" states. |
announcementPriority | announcement-priority | 'polite' | 'assertive' | 'polite' | ARIA live region politeness level for screen reader announcements. - polite: Announcements wait for current reading to complete (recommended) - assertive: Announcements interrupt current reading |
role | role | 'menu' | 'listbox' | 'tree' | 'grid' | 'dialog' | 'tooltip' | 'none' | null | ARIA role for the popover element. Should match the semantic purpose of the content. |
describedBy | described-by | string | undefined | — | ID of an element that describes the popover content. Applied as aria-describedby on the popover container. |
focusSettled | — | Promise<void> | — | Resolves once the current open's initial-focus sequence has settled — discovery ran and focus was applied, or the sequence was skipped (tooltip role, prevented request-show / initial-focus, closed before the deferred work ran). Before the first open it is already resolved. |
Events
| Event | Detail | Description |
|---|---|---|
request-show | — | Emitted before opening; cancelable to prevent open. |
show | — | Emitted after the popover opens. |
request-hide | — | Emitted before closing; cancelable to prevent close. |
hide | — | Emitted after the popover closes. |
initial-focus | — | Fired on show (non-tooltip role) before the autofocus discovery runs. Cancelable: call event.preventDefault() to skip the default focus and apply your own. Detail: HyInitialFocusDetail |
Slots
| Slot | Description |
|---|---|
trigger | The element that triggers the popover (automatically toggles on click) |
default | The content to display inside the popover. |
CSS Parts
| Part | Description |
|---|---|
base | The popover container element (has popover attribute). |
body | The visible content wrapper with background and border. |
arrow | The arrow indicator element. |
live-region | The ARIA live region for screen reader announcements (visually hidden). |
CSS Custom Properties
| Property | Description |
|---|---|
--hy-popover-max-width | Maximum width of the popover |
--hy-popover-padding | Inner padding of the popover body |
--hy-popover-radius | Border radius |
--hy-popover-stroke | Border width |
--hy-popover-border-default-rest | Border color |
--hy-popover-ornament | Size of the arrow indicator |
--hy-popover-shadow | Box shadow for the popover body |
Methods
show()
Shows the popover. Emits request-show before opening (cancelable). Emits show after opened.
hide()
Hides the popover. Emits request-hide before closing (cancelable). Emits hide after closed.
toggle()
Toggles the popover open/closed state. Emits the appropriate request event (cancelable) before toggling.
reposition()
Manually triggers a position update. Useful after content changes that affect popover size.
focus()
Sets focus on the popover element or its first focusable child.
blur()
Removes focus from the popover.