OverlaysReact
A popup that opens on top of the entire page.
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:
<Dialog.Root>
<Dialog.Trigger />
<Dialog.Portal>
<Dialog.Backdrop />
<Dialog.Popup>
<Dialog.Title />
<Dialog.Description />
<Dialog.Close />
</Dialog.Popup>
</Dialog.Portal>
</Dialog.Root>
// Not in this example: Dialog.ViewportExamples
Nested
A dialog opens another; the parent styles itself with data-nested-dialog-open and --nested-dialogs.
Focus Management
initialFocus and finalFocus choose where focus goes on open and on close.
Outside Scroll
Viewport scrolls and the popup grows past the bottom edge.
Inside Scroll
Viewport positions the popup; a container inside it scrolls.
Uncontained
Content that looks outside the popup stays inside Popup, so it keeps its tab order.
Detached Triggers Simple
A trigger outside Root opens it through a handle.
Detached Triggers Controlled
Several triggers share one controlled dialog and pass a payload.
Open From Menu
A menu item opens the dialog.
API reference
21 props across 9 parts, in React syntax. className, style, render are listed once, under Shared props.
BackdropDialog.Backdrop
An overlay displayed beneath the popup. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
forceRenderWhether the backdrop is forced to render even when nested. | boolean | false |
| Data attribute | Description |
|---|---|
data-open | Present when the dialog is open. |
data-closed | Present when the dialog is closed. |
data-starting-style | Present when the dialog begins animating in. |
data-ending-style | Present when the dialog is animating out. |
CloseDialog.Close
A button that closes the dialog. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
| Data attribute | Description |
|---|---|
data-disabled | Present when the button is disabled. |
DescriptionDialog.Description
A paragraph with additional information about the dialog. Renders a <p>.
PopupDialog.Popup
A container for the dialog contents. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
initialFocusDetermines the element to focus when the dialog is opened. | boolean | React.RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null) | — |
finalFocusDetermines the element to focus when the dialog is closed. | boolean | React.RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null) | — |
| Data attribute | Description |
|---|---|
data-open | Present when the dialog is open. |
data-closed | Present when the dialog is closed. |
data-starting-style | Present when the dialog begins animating in. |
data-ending-style | Present when the dialog is animating out. |
data-nested | Present when the dialog is nested within another dialog. |
data-nested-dialog-open | Present when the dialog has other open dialogs nested within it. |
| CSS variable | Description |
|---|---|
--nested-dialogs | Indicates how many dialogs are nested within. |
PortalDialog.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 |
RootDialog.Root
Groups all parts of the dialog. Doesn’t render its own element.
| Prop | Type | Default |
|---|---|---|
defaultOpenWhether the dialog is initially open. | boolean | false |
openWhether the dialog is currently open. | boolean | — |
onOpenChangeEvent handler called when the dialog is opened or closed. | ((open: boolean, eventDetails: DialogRootChangeEventDetails) => void) | — |
actionsRefA ref to imperative actions. | React.RefObject<DialogRootActions | null> | — |
defaultTriggerIdID of the trigger that the dialog is associated with. | string | null | — |
disablePointerDismissalWhether to prevent the dialog from closing on outside presses. | boolean | false |
handleA handle to associate the dialog with a trigger. | DialogHandle<Payload> | — |
modalDetermines if the dialog enters a modal state when open. | boolean | "trap-focus" | true |
onOpenChangeCompleteEvent handler called after any animations complete when the dialog is opened or closed. | ((open: boolean) => void) | — |
triggerIdID of the trigger that the dialog is associated with. | string | null | — |
childrenThe content of the dialog. | React.ReactNode | PayloadChildRenderFunction<Payload> | — |
TitleDialog.Title
A heading that labels the dialog. Renders a <h2>.
TriggerDialog.Trigger
A button that opens the dialog. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
handleA handle to associate the trigger with a dialog. | DialogHandle<Payload> | — |
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
payloadA payload to pass to the dialog when it is opened. | Payload | — |
idID of the trigger. | string | — |
| Data attribute | Description |
|---|---|
data-disabled | Present when the trigger is disabled. |
data-popup-open | Present when the corresponding dialog is open. |
ViewportDialog.Viewport
A positioning container for the dialog popup that can be made scrollable. Renders a <div>.
| Data attribute | Description |
|---|---|
data-open | Present when the dialog is open. |
data-closed | Present when the dialog is closed. |
data-starting-style | Present when the dialog begins animating in. |
data-ending-style | Present when the dialog is animating out. |
data-nested | Present when the dialog is nested within another dialog. |
data-nested-dialog-open | Present when the dialog has other open dialogs nested within it. |
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 |
|---|---|---|
| Escape | Closes the dialog and returns focus to the trigger | Popup |
| Escape | With several dialogs open, closes only the one on top | Popup |
| TabShiftTab | Moves between the popup's controls; focus wraps inside while modal is on | Popup |
Accessibility
| Part | Sets | So that |
|---|---|---|
| Trigger | aria-haspopup="dialog", aria-expanded, aria-controls | a screen reader says the button opens a dialog and whether it is open |
| Popup | role="dialog" | the popup is announced as a dialog |
| Popup | aria-labelledby to Title, aria-describedby to Description | the dialog is announced with its heading and its description |
| Popup | aria-hidden="true" on the rest of the page while modal is on | a screen reader stays inside the dialog |
| Backdrop | role="presentation" | the overlay is not read |
| Viewport | role="presentation" | the positioning container is not read |
Focus order
- Opening moves focus to the first focusable element in Popup (to Popup itself when opened by touch), or to
initialFocus. TabandShift+Tabmove through the popup's controls and wrap at either end while the dialog is modal.- Closing returns focus to the Trigger that opened it, or to
finalFocus.