Overlays
Dialog
A modal panel. Focus trapping, scroll locking, Escape and the backdrop click all come from base-react, so this layer only adds the shape.
Basic
Open it, then press Tab and Escape.
<Dialog.Root> <Dialog.Trigger asChild><Button>Publish</Button></Dialog.Trigger> <Dialog.Content> <Dialog.Header> <Dialog.Title>Publish style-tokens?</Dialog.Title> <Dialog.Description> This uploads 0.1.0 to the public npm registry. Published versions cannot be replaced. </Dialog.Description> </Dialog.Header> <Dialog.Body> <Alert.Root tone="warning"> <Alert.Icon /> <Alert.Body> <Alert.Description> Two packages depend on this one at workspace:^ and will need rebuilding. </Alert.Description> </Alert.Body> </Alert.Root> </Dialog.Body> <Dialog.Footer> <Dialog.Close asChild><Button variant="secondary">Cancel</Button></Dialog.Close> <Dialog.Close asChild><Button>Publish</Button></Dialog.Close> </Dialog.Footer> </Dialog.Content></Dialog.Root>Tab through it. Focus never leaves the dialog and returns to the trigger on close, and the page behind does not scroll. None of that is written here; it is why the base layer exists.
Size
Use size to set the width. The body scrolls, not the page, so the actions never leave the screen.
{(['s', 'm', 'l'] as const).map(size => ( <Dialog.Root key={size}> <Dialog.Trigger asChild> <Button variant="secondary">size {size}</Button> </Dialog.Trigger> <Dialog.Content size={size}> <Dialog.Header> <Dialog.Title>Size {size}</Dialog.Title> <Dialog.Description>400, 520 and 720px at most.</Dialog.Description> </Dialog.Header> <Dialog.Footer> <Dialog.Close asChild><Button variant="secondary">Close</Button></Dialog.Close> </Dialog.Footer> </Dialog.Content> </Dialog.Root>))}Dialog.Title is not really optional. Without it the dialog has nothing to announce itself as, and Radix warns. If the design has no visible heading, wrap the title in VisuallyHidden rather than dropping it.Parts
Import it as a namespace, and the parts work on either side of the server boundary.
import * as Dialog from '@minuk-hwang-design-system/components-react/dialog';| Part | What it is |
|---|---|
Dialog.Root | Owns the open state. |
Dialog.Trigger | Opens it. Use asChild to keep your own button. |
Dialog.Content | The panel, with the scrim and the close button. |
Dialog.Header | Title and description. Padded to clear the close button. |
Dialog.Title | The accessible name and the visible heading, same node. A Heading, so it takes every prop of one. |
Dialog.Description | Announced with the title on open. A Text, so it takes every prop of one, including leading="reading". |
Dialog.Body | The scrolling region. |
Dialog.Footer | Actions, aligned right. |
Dialog.Close | Closes it from anywhere inside. |
Props
| Prop | Type | Default | Notes |
|---|---|---|---|
open | boolean | — | Controlled state, on Root. |
onOpenChange | (open: boolean) => void | — | On Root. Fires for every way out, including Escape and the scrim. |
size | 's' | 'm' | 'l' | 'm' | On Content. 400, 520 or 720px. |
hideClose | boolean | false | Only if a footer action closes the dialog. |