Dialog
Modal dialog built on the native dialog element.
- Browsers
- Chrome 123+, Firefox 121+, Safari 17.5+
- Tier
- 0
- Bundle compressed
- 940 B
- Forced layout budget
- 0 ms
- Axe budget
- 0
- SSR compatible
- Yes
- Published contract
- Test harness
Playground
import { Dialog } from "@gunkin/uiify";
export default function Example() {
return (
<Dialog
id="settings-dialog"
trigger="Open settings"
title="Settings"
description="Update your profile preferences."
/>
);
}API
Dialog
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | — | — |
trigger | ReactNode | — | — |
title | ReactNode | — | — |
description | ReactNode | — | — |
children | ReactNode | — | — |
closeLabel | string | — | Accessible name of the header close icon. |
labels | Partial<DialogLabels> | — | — |
footer | ReactNode | — | Actions row pinned under the body. |
slotProps | DialogSlotProps | — | Native props for the elements created by this convenience component. |
size | DialogSize | — | Width step; `full` fills the viewport. Default `md`. |
align | DialogAlign | — | Vertical position on tablet and desktop; phones get a bottom sheet unless `size` is `full`. Default `center`. |
dismiss | DialogDismiss | — | Native `closedby`: `any` also closes on a backdrop tap, `closerequest` on Escape only, `none` only through your own controls. Default `any`. Engines without `closedby` (Safari) ignore it: Escape still closes the dialog there, and `UIEnhance` honours it only for its own backdrop tap (`any` only) and sheet swipe (off for `none`). |
backdrop | DialogBackdrop | — | Backdrop treatment. Default `opaque`. |
DialogBody
| Prop | Type | Default | Description |
|---|
DialogClose
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | — |
target | string | — | `id` of the `DialogContent` this button closes. |
command | string | — | — |
commandfor | string | — | — |
as | ElementType & ("button" | ComponentType<any>) | — | `"button"` (default) or a component that forwards props and ref to a native <button>. |
render | never | — | — |
DialogContent
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | — |
id | string | — | Unique id supplied by the consumer; referenced by trigger and close `target`. |
size | DialogSize | — | Width step; `full` fills the viewport. Default `md`. |
align | DialogAlign | — | Vertical position on tablet and desktop; phones get a bottom sheet unless `size` is `full`. Default `center`. |
dismiss | DialogDismiss | — | Native `closedby`: `any` also closes on a backdrop tap, `closerequest` on Escape only, `none` only through your own controls. Default `any`. Engines without `closedby` (Safari) ignore it: Escape still closes the dialog there, and `UIEnhance` honours it only for its own backdrop tap (`any` only) and sheet swipe (off for `none`). |
backdrop | DialogBackdrop | — | Backdrop treatment. Default `opaque`. |
DialogDescription
| Prop | Type | Default | Description |
|---|
DialogFooter
| Prop | Type | Default | Description |
|---|
DialogGrabber
| Prop | Type | Default | Description |
|---|
DialogHeader
| Prop | Type | Default | Description |
|---|
DialogTitle
| Prop | Type | Default | Description |
|---|
DialogTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | — |
target | string | — | `id` of the `DialogContent` this button opens; the browser resolves it at click time. |
command | string | — | — |
commandfor | string | — | — |
as | ElementType & ("button" | ComponentType<any>) | — | `"button"` (default) or a component that forwards props and ref to a native <button>. |
render | never | — | — |
Keyboard
| Key | Action |
|---|---|
| Escape | Close (only the topmost modal when nested) |
| Tab | Cycle focus inside modal |
| Shift+Tab | Reverse tab order |
Accessibility
Native <dialog> opened by a command="show-modal" invoker before any JavaScript runs. The browser owns modality, focus containment, Escape and focus return; closing uses request-close so onCancel can veto. The header close icon is always present. On phones the modal is a bottom sheet; swiping it down and tapping the backdrop are shortcuts, never the only way out.
Styling hooks
data-size="sm|lg|full"data-align="start"data-backdrop="blur|none"--uiify-trigger-openinside .uiify-dialog-root--uiify-dialog-inline-sizedialog width