OverlaysReact
An accessible popup anchored to a button.
Rendered by React 19.3.1
Loading React…Installation
npm i @hamur/reactNot on npm yet. The package goes out under the chosen name.
Anatomy
Import the component and assemble its parts:
<Popover.Root>
<Popover.Trigger />
<Popover.Portal>
<Popover.Positioner>
<Popover.Popup>
<Popover.Arrow />
<Popover.Title />
<Popover.Description />
</Popover.Popup>
</Popover.Positioner>
</Popover.Portal>
</Popover.Root>
// Not in this example: Popover.Backdrop, Popover.Close, Popover.ViewportExamples
Open On Hover
openOnHover and delay open the popover when the pointer rests on the trigger.
Detached Triggers Simple
A trigger outside Root opens it through a handle.
Detached Triggers Controlled
open with triggerId lets several triggers share one popover.
Detached Triggers Full
Viewport animates the content when another trigger opens the same popover.
API reference
35 props across 11 parts, in React syntax. className, style, render are listed once, under Shared props.
ArrowPopover.Arrow
Displays an element positioned against the popover anchor. Renders a <div>.
| Data attribute | Description |
|---|---|
data-open | Present when the popup is open. |
data-closed | Present when the popup is closed. |
data-side | Indicates which side the popup is positioned relative to the trigger. |
data-align | Indicates how the popup is aligned relative to specified side. |
data-uncentered | Present when the popover arrow is uncentered. |
BackdropPopover.Backdrop
An overlay displayed beneath the popover. Renders a <div>.
| Data attribute | Description |
|---|---|
data-open | Present when the popup is open. |
data-closed | Present when the popup is closed. |
data-starting-style | Present when the popup begins animating in. |
data-ending-style | Present when the popup is animating out. |
ClosePopover.Close
A button that closes the popover. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
DescriptionPopover.Description
A paragraph with additional information about the popover. Renders a <p>.
PopupPopover.Popup
A container for the popover contents. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
initialFocusDetermines the element to focus when the popover is opened. | boolean | React.RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null) | — |
finalFocusDetermines the element to focus when the popover is closed. | boolean | React.RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null) | — |
| Data attribute | Description |
|---|---|
data-open | Present when the popup is open. |
data-closed | Present when the popup is closed. |
data-starting-style | Present when the popup begins animating in. |
data-ending-style | Present when the popup is animating out. |
data-side | Indicates which side the popup is positioned relative to the trigger. |
data-align | Indicates how the popup is aligned relative to specified side. |
data-instant | Present if animations should be instant. |
| CSS variable | Description |
|---|---|
--popup-width | The width of the popup. |
--popup-height | The height of the popup. |
PortalPopover.Portal
A portal element that moves the popup to a different part of the DOM. By default, the portal element is appended to <body>. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
containerA parent element to render the portal element into. | HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null | — |
keepMountedWhether to keep the portal mounted in the DOM while the popup is hidden. | boolean | false |
PositionerPopover.Positioner
Positions the popover against the trigger. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
disableAnchorTrackingWhether to disable the popup from tracking any layout shift of its positioning anchor. | boolean | false |
alignHow to align the popup relative to the specified side. | Align | 'center' |
alignOffsetAdditional offset along the alignment axis in pixels. | number | OffsetFunction | 0 |
sideWhich side of the anchor element to align the popup against. | Side | 'bottom' |
sideOffsetDistance between the anchor and the popup in pixels. | number | OffsetFunction | 0 |
arrowPaddingMinimum distance to maintain between the arrow and the edges of the popup. | number | 5 |
anchorAn element to position the popup against. | Element | VirtualElement | React.RefObject<Element | null> | (() => Element | VirtualElement | null) | null | — |
collisionAvoidanceDetermines how to handle collisions when positioning the popup. | CollisionAvoidance | — |
collisionBoundaryAn element or a rectangle that delimits the area that the popup is confined to. | Boundary | 'clipping-ancestors' |
collisionPaddingAdditional space to maintain from the edge of the collision boundary. | Padding | 5 |
stickyWhether to maintain the popup in the viewport after | boolean | false |
positionMethodDetermines which CSS position property to use. | "absolute" | "fixed" | 'absolute' |
| Data attribute | Description |
|---|---|
data-open | Present when the popup is open. |
data-closed | Present when the popup is closed. |
data-anchor-hidden | Present when the anchor is hidden. |
data-side | Indicates which side the popup is positioned relative to the trigger. |
data-align | Indicates how the popup is aligned relative to specified side. |
| CSS variable | Description |
|---|---|
--available-width | The available width between the trigger and the edge of the viewport. |
--available-height | The available height between the trigger and the edge of the viewport. |
--anchor-width | The anchor's width. |
--anchor-height | The anchor's height. |
--transform-origin | The coordinates that this element is anchored to. Used for animations and transitions. |
--positioner-width | The width of the popover's positioner.
It is important to set width to this value when using CSS to animate size changes. |
--positioner-height | The height of the popover's positioner.
It is important to set height to this value when using CSS to animate size changes. |
RootPopover.Root
Groups all parts of the popover. Doesn’t render its own element.
| Prop | Type | Default |
|---|---|---|
defaultOpenWhether the popover is initially open. | boolean | false |
openWhether the popover is currently open. | boolean | — |
onOpenChangeEvent handler called when the popover is opened or closed. | ((open: boolean, eventDetails: PopoverRootChangeEventDetails) => void) | — |
actionsRefA ref to imperative actions. | React.RefObject<PopoverRootActions | null> | — |
defaultTriggerIdID of the trigger that the popover is associated with. | string | null | — |
handleA handle to associate the popover with a trigger. | PopoverHandle<Payload> | — |
modalDetermines if the popover enters a modal state when open. | boolean | "trap-focus" | false |
onOpenChangeCompleteEvent handler called after any animations complete when the popover is opened or closed. | ((open: boolean) => void) | — |
triggerIdID of the trigger that the popover is associated with. | string | null | — |
childrenThe content of the popover. | React.ReactNode | PayloadChildRenderFunction<Payload> | — |
TitlePopover.Title
A heading that labels the popover. Renders a <h2>.
TriggerPopover.Trigger
A button that opens the popover. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
handleA handle to associate the trigger with a popover. | PopoverHandle<Payload> | — |
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
payloadA payload to pass to the popover when it is opened. | Payload | — |
openOnHoverWhether the popover should also open when the trigger is hovered. | boolean | false |
delayHow long to wait before the popover may be opened on hover. | number | 300 |
closeDelayHow long to wait before closing the popover that was opened on hover. | number | 0 |
idID of the trigger. | string | — |
| Data attribute | Description |
|---|---|
data-popup-open | Present when the corresponding popover is open. |
data-pressed | Present when the trigger is pressed. |
data-disabled | Present when the trigger is disabled. |
ViewportPopover.Viewport
A viewport for displaying content transitions. This component is only required if one popup can be opened by multiple triggers, its content changes based on the trigger, and switching between them is animated. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
childrenThe content to render inside the transition container. | React.ReactNode | — |
| Data attribute | Description |
|---|---|
data-current | Applied to the direct child of the viewport when no transitions are present or the new content when it's entering. |
data-previous | Applied to the direct child of the viewport that contains the exiting content when transitions are present. |
data-activation-direction | Indicates the direction from which the popup was activated. This can be used to create directional animations based on how the popup was triggered. Contains space-separated values for both horizontal and vertical axes. |
data-transitioning | Indicates that the viewport is currently transitioning between old and new content. |
data-instant | Present if animations should be instant. |
| CSS variable | Description |
|---|---|
--popup-width | The width of the parent popup. This variable is placed on the 'previous' container and stores the width of the popup when the previous content was rendered. It can be used to freeze the dimensions of the popup when animating between different content. |
--popup-height | The height of the parent popup. This variable is placed on the 'previous' container and stores the height of the popup when the previous content was rendered. It can be used to freeze the dimensions of the popup when animating between different content. |
Shared props
Every part that renders an element takes these.
| Prop | Type |
|---|---|
classNameCSS class applied to the element, or a function that
returns a class based on the component's state. | string | ((state: State) => string | undefined) |
styleStyle applied to the element, or a function that
returns a style object based on the component's state. | React.CSSProperties | ((state: State) => React.CSSProperties | undefined) |
renderAllows you to replace the component's HTML element
with a different tag, or compose it with another component. | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State> |
Keyboard and accessibility
Keyboard
| Keys | Does | Part |
|---|---|---|
| EnterSpace | Opens and closes the popover, also when Trigger renders an element other than <button> | Trigger |
| Escape | Closes the popover | Popup |
| Tab | From the last element in the popup, moves on to the element after the trigger | Popup |
| ShiftTab | From the popup, moves back to the trigger | Popup |
Accessibility
| Part | Sets | So that |
|---|---|---|
| Trigger | aria-haspopup="dialog", aria-expanded, aria-controls | a screen reader says the button opens a popup and whether it is open |
| Popup | role="dialog", aria-labelledby to Title, aria-describedby to Description | the popup is announced with its heading and description |
| Popup | sits right after the trigger in the tab order, wherever it is portaled | keyboard users reach the content from the button that opened it |
| Arrow | aria-hidden="true" | the decorative arrow is not read |
| Backdrop | role="presentation" | the overlay is not read |
Focus order
- Opening with a click or a key moves focus into Popup, or to
initialFocus; opening on hover leaves focus where it was. Tabmoves through the popup and then to the element after Trigger;Shift+Tabgoes back to Trigger.- Closing returns focus to Trigger, or to
finalFocus.