Popover

A headless, accessible popover with smart positioning, optional arrow, and optional modal focus trap.

Features

  • Controlled or uncontrolled
  • Modal or non-modal
  • Flexible placement
  • Optional arrow
  • Automatic focus management
  • Escape and outside-click dismissal
  • Portal rendering
  • Accessible labelling

Installation

npm install @ariaui/popover

Examples

Anchored floating surface with optional arrow and optional modal focus trap. Positioning stays in sync with the trigger.

Popover

A headless, accessible popover example.

Preview

No preview output yet.

live.tsxReady

Framer Motion

A popover content surface animated by Framer Motion through asChild composition.

Preview

No preview output yet.

live.tsxReady

Anatomy

tsx
import * as Popover from "@ariaui/popover";

export default function Example() {
  return (
    <Popover.Root>
      <Popover.Trigger />
      <Popover.Content>
        <Popover.Heading />
        <Popover.Description />
        <Popover.Close />
      </Popover.Content>
    </Popover.Root>
  );
}

API Reference

Root

Context provider and state container for the popover. Manages open state, positioning, and modal behavior.

PropTypeDefault
open
boolean
defaultOpen
booleanfalse
onOpenChange
(open: boolean) => void
modal
booleanfalse
placement
'top' | 'right' | 'bottom' | 'left' | '<side>-start' | '<side>-end''bottom'
offset
number0

Trigger

Button that toggles the popover open and closed.

AttributeValues
aria-haspopupdialog
aria-expandedtrue when open
aria-controlsID of the Content element
[data-state]open | closed

Content

Floating panel containing the popover body. Portals to document.body, positions itself against Trigger, and unmounts when closed.

PropTypeDefault
asChild
booleanfalse
arrow
booleanfalse
arrowClassName
string
loop
booleantrue
AttributeValues
roledialog
aria-modaltrue when modal
aria-labelledbyID of the Heading
aria-describedbyID of the Description
[data-side]top | right | bottom | left

Heading

Accessible title for Content. Registered as aria-labelledby on Content.

PropTypeDefault
asChild
booleanfalse

Description

Accessible description for Content. Registered as aria-describedby on Content.

Close

Button that closes the popover when clicked.

Keyboard

ShortcutAction
Enter/SpaceWhen focus is on Trigger, opens the popover and moves focus to the first focusable element in Content.
TabWhen the popover is open, moves focus to the next focusable element. In modal mode, focus cycles within Content.
Shift+TabMoves focus to the previous focusable element. In modal mode, focus cycles within Content.
EscCloses the popover and returns focus to the Trigger.

Accessibility

Popover follows the WAI-ARIA Dialog (Modal) pattern with a non-modal mode for lightweight surfaces:

  • Trigger exposes aria-haspopup="dialog", aria-expanded, and aria-controls pointing at Content.
  • Content renders with role="dialog", aria-modal matching the modal prop, and data-side for styling.
  • Heading registers its id as aria-labelledby on Content; Description registers as aria-describedby.
  • When modal is true, focus is trapped in Content and interaction with the rest of the page is blocked.
  • When modal is false, focus moves into Content on open but can leave naturally via Tab.
  • Escape closes the popover and returns focus to the Trigger.
  • Content is portaled to document.body, avoiding ancestor clipping and stacking issues.
Previous
Pagination