Dialog
A modal dialog that dims the page, traps focus and hands it back when it closes.
import { Dialog } from "@butteryui/react";Demo
When to use it
Use it for
- A decision or a short task to finish before going on: confirm a delete, rename a coffee
- Something that needs full attention for a moment, such as an announcement
Not for
- A delete or another step that can't be undone, said in full with what it takes with it: use ConsequenceDialog
- News that needs no answer: use a toast
- Settings beside the work: use a Popover or the Inspector
- Errors in a form: show them next to the field
Examples
Delete confirm
A confirmation before something can't be undone, with the safe choice first, where focus lands.
Delete from list
One dialog for a whole list, opened from state: deleting a roast hands focus to the next roast's Delete.
3 roasts on the shelf
Lantern Hill
Harbor Fog
Ironwood Night
Invite teammate
A team list whose Invite button opens a short form; sending it adds the person to the list.
Head roaster
Shipping
Rename dialog
A short task in a controlled dialog: rename, then close it from the form's submit.
Variants and states
Confirm
Fades in over dur-fast from 97%; Escape, the close button or a press outside closes it, and focus goes back to the trigger.
With media
The media sits 8px in, its corners 10px inside the dialog's 18px; the close button sits over it.
Sizes
Props
Dialog
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | Whether it's open. Pair with onOpenChange to control it. | |
defaultOpen | boolean | false | Whether it starts open when uncontrolled. Defaults to false. |
onOpenChange | ((open: boolean) => void) | Called when it opens or closes: its trigger, a close button, Escape or a press outside. | |
children | ReactNode | The trigger and the content. |
Dialog.Actions
It also takes ComponentPropsWithRef<"div">.
Dialog.Body
It also takes ComponentPropsWithRef<"div">.
Dialog.Close
It also takes ComponentPropsWithRef<typeof RadixDialog.Close>.
Dialog.Content
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "md" | "lg" | "full" | "sm" | How wide it is: "sm" (400px, the default), "md" (560px), "lg" (880px) or "full", the whole window
less 16px on every side, which fades in and out without growing. |
label | string | Names the dialog for screen readers when it has no Dialog.Text title to name it. | |
description | ReactNode | Describes the dialog for screen readers when it has no Dialog.Text body to describe it. | |
showClose | boolean | true | Whether it shows a close button in its top corner. Defaults to true. |
closeLabel | string | "Close" | Names the close button. Defaults to "Close". |
closeDisabled | boolean | false | Shows the close button disabled (aria-disabled, still focusable) and stops it closing the dialog, such
as while a submit is pending. Defaults to false. |
It also takes Omit<ComponentPropsWithRef<typeof RadixDialog.Content>, "forceMount" | "asChild">.
Dialog.Description
It also takes ComponentPropsWithRef<"p">.
Dialog.Media
It also takes ComponentPropsWithRef<"div">.
Dialog.Text
| Prop | Type | Default | Description |
|---|---|---|---|
titlerequired | ReactNode | The dialog's title, which names it for screen readers. | |
body | ReactNode | A line or two under the title, which describes the dialog for screen readers. | |
caption | ReactNode | A short note under the body, such as a keyboard tip. | |
align | "start" | "center" | "start" | "start" (the default) or "center". |
size | "dialog" | "feature" | "dialog" | "dialog" (the default), a dialog title, or "feature", a title-lg title as an announcement has. |
It also takes Omit<ComponentPropsWithRef<"div">, "title">.
Dialog.Trigger
It also takes ComponentPropsWithRef<typeof RadixDialog.Trigger>.
Accessibility
A modal dialog (aria-modal) named by its Dialog.Text title, or by label without one, and described by the text's body. Once it has settled, focus moves to its first field to type in, else its first control, its actions included, so a confirm with the safe choice first lands on that; with no control it stays on the dialog, so the close button's tooltip stays shut. Tab stays inside; Escape, the close button or a press outside closes it and focus goes back to what opened it, or, when that's gone, wherever onCloseAutoFocus sends it, as in the list example. The page behind is hidden from screen readers while it's open.
| Keys | What they do |
|---|---|
| Enter or Space | Opens it from its trigger |
| Tab or Shift+Tab | Moves to the next or previous control inside it, going round so focus never leaves it |
| Escape | Closes it and hands focus back to what opened it |
Motion
It appears with a `Presence` preset and leaves faster than it came, in about 60% of the time.
With reduced motion: It and its scrim fade in and out over 100ms, without growing or rising.
Layout
Dialog.Content is 400px wide (--bt-w-dialog) at size="sm", the default, 560 at "md" and 880 at "lg", never wider than the window less 16px a side, and "full" fills the window less 16px a side. Its height follows its content up to the window's, past which the content scrolls inside it. Dialog.Actions sets its buttons in equal columns 8px apart, and size="cta" makes them 48px tall. The close button is a 28px IconButton centered 24px in from the top and end edges.
Related
See it in the Halfmoon shop demo.