OPERATOR

Popover

Interactive details

Activate the details button to move focus into the named surface. Background controls remain available, and Tab or Shift+Tab can leave the surface. Pointer movement does not close interactive content.

<Popover
  title="Equipment details"
  description="Pump P-042 · report current"
  content={<Button onClick={inspect}>Inspect equipment</Button>}
>
  <Button>Equipment details</Button>
</Popover>

Supply one focusable element that forwards its ref and DOM event props. Use title for a visible heading or aria-label for a name without a visible heading. The forwarded Popover ref targets the role="dialog" container.

Controlled state

open and onOpenChange allow the host to accept or decline each request. The dialog and expanded state follow the supplied open value. Use defaultOpen for an initially open uncontrolled surface.

<Popover
  title="Equipment details"
  open={open}
  onOpenChange={setOpen}
  content={<EquipmentDetails />}
>
  <Button>Details</Button>
</Popover>

Focus and dismissal

Escape, the supplied Close button, trigger reactivation and an outside pointer interaction request dismissal. Escape and Close restore focus to a surviving trigger. When an outside interaction or keyboard navigation moves focus to another control, dismissal preserves that destination. A nested OPERATOR menu or Popover handles Escape before its parent.

Ancestor scrolling dismisses the surface when the trigger moves or the user scrolls explicitly. Automatic browser scroll correction that leaves the trigger in the same screen position preserves the open surface and its focus.

If an open trigger is removed, returnFocusRef supplies a fallback such as the results region. That target must be focusable, for example a named region with tabIndex={-1}. onCloseAutoFocus receives a cancelable native Event after an accepted close or unmount of an open Popover; event.preventDefault() suppresses automatic restoration so the host can transfer focus to an Inspector.

<Popover
  title="Equipment details"
  open={open}
  onOpenChange={setOpen}
  returnFocusRef={resultsRef}
  onCloseAutoFocus={(event) => {
    if (pinning) event.preventDefault();
  }}
  content={<EquipmentDetails onPin={pinDetails} />}
>
  <Button>Details</Button>
</Popover>

Description

description is a short text summary that reaches every user:

  • Hovering the trigger, or focusing it with the keyboard, previews it in a Tooltip on the same trigger. Popover preserves the trigger's ref, existing handlers and ARIA relationships.
  • Opening the details shows it as a dim line under the title, and it becomes the dialog's accessible description; the preview is suppressed while the details are open.
  • When the Popover is disabled, the description is the only explanation, so the trigger stays reachable. With a Button trigger, the Button stays in the Tab order, announced as unavailable; focus or a tap shows the description, Escape or a tap elsewhere hides it, and the details never open.

The summary contains text only. Keep identity and critical condition visible in the surrounding result.

Placement and motion

side accepts top, right, bottom or left; align accepts start, center or end. Defaults are bottom and start. React Aria flips and shifts placement within the viewport. Above or below the trigger, start and end line up the left and right edges (mirrored in right-to-left text); beside it, on the left or right side, start lines up the top edges and end the bottom edges. A surface that flips to the other side keeps its alignment. It always stays at least 16 px inside the screen edges: where an aligned surface would run off the screen, it slides along the trigger instead; a tall surface is capped 16 px from the top or bottom; and beside the trigger it stops 16 px short of the far screen edge. The surface wraps text at about 30rem and is never wider than the viewport; it scrolls when its height is constrained. className styles the content surface. To widen one surface, set --oprt-popover-max-width through it, for example className="[--oprt-popover-max-width:48rem]"; the viewport limit still applies.

Popover follows shared OPERATOR motion and reduced-motion settings. motion="none" opts out explicitly. Retained closing content is inert and hidden from assistive interaction, and reopening focuses the named dialog again.

Disabled

With a description, a disabled Popover's Button trigger stays focusable, so the reason it is unavailable can be read by keyboard focus, a tap or hover:

Props

PROPTYPEDEFAULTREQUIRED
title

Visible heading that names the dialog; required unless aria-label is set.

string—no
aria-label

Name without a visible heading; required unless title is set.

string—no
openboolean—no
defaultOpenbooleanfalseno
onOpenChangefunction—no
sideenum
top | right | bottom | left
"bottom"no
alignenum
start | center | end
"start"no
disabledbooleanfalseno
motionenum
auto | none
"auto"no
description

Short summary: a tooltip preview on the trigger and a line under the title of the open surface, which it describes. A disabled trigger with a description stays focusable, so focus or a tap shows it.

string—no
returnFocusRef

RefObject<HTMLElement | null> fallback when the trigger is removed.

node—no
onCloseAutoFocusfunction—no
classNamestring—no

Accessibility

  • Enter, Space, pointer and touch explicitly activate the trigger.
  • The trigger exposes connected expanded and dialog relationships.
  • The dialog receives focus on opening and uses the OPERATOR focus indicator, with a system outline in forced colors.
  • The surface is nonmodal: background content remains available and focus is not trapped.
  • A visible Close button, Escape and outside interaction provide dismissal.
  • Controlled requests do not restore focus until the host accepts the close.
  • Passive descriptions contain no interactive controls and remain supplemental. The open dialog is described by the description.
  • A disabled Button trigger with a description stays focusable and is announced as unavailable, so keyboard and touch users can reach the explanation.

Manifest entry

<Popover title="Equipment details" description="Equipment operational" content={<button>Inspect</button>}><Button>Details</Button></Popover>
View agent manifest
{
  "description": "Named nonmodal interactive details anchored to an explicitly activated trigger.",
  "props": {
    "title": {
      "type": "string",
      "description": "Visible heading that names the dialog; required unless aria-label is set."
    },
    "aria-label": {
      "type": "string",
      "description": "Name without a visible heading; required unless title is set."
    },
    "open": {
      "type": "boolean"
    },
    "defaultOpen": {
      "type": "boolean",
      "default": false
    },
    "onOpenChange": {
      "type": "function"
    },
    "side": {
      "type": "enum",
      "values": [
        "top",
        "right",
        "bottom",
        "left"
      ],
      "default": "bottom"
    },
    "align": {
      "type": "enum",
      "values": [
        "start",
        "center",
        "end"
      ],
      "default": "start"
    },
    "disabled": {
      "type": "boolean",
      "default": false
    },
    "motion": {
      "type": "enum",
      "values": [
        "auto",
        "none"
      ],
      "default": "auto"
    },
    "description": {
      "type": "string",
      "description": "Short summary: a tooltip preview on the trigger and a line under the title of the open surface, which it describes. A disabled trigger with a description stays focusable, so focus or a tap shows it."
    },
    "returnFocusRef": {
      "type": "node",
      "description": "RefObject<HTMLElement | null> fallback when the trigger is removed."
    },
    "onCloseAutoFocus": {
      "type": "function"
    },
    "className": {
      "type": "string"
    }
  },
  "slots": {
    "children": {
      "type": "single focusable trigger element",
      "required": true
    },
    "content": {
      "type": "node",
      "required": true
    }
  },
  "composable_with": [
    "Button",
    "EntityPreview",
    "DescriptionList",
    "CopyButton",
    "Inspector",
    "Tooltip"
  ],
  "never_inside": [
    "Tooltip"
  ],
  "example": "<Popover title=\"Equipment details\" description=\"Equipment operational\" content={<button>Inspect</button>}><Button>Details</Button></Popover>"
}

On this page