Dialog
Dialog wraps Spar's headless dialog primitive and adds Takeoff styling hooks
for overlay intensity, header types, and compound anatomy.
Usage
import { Dialog } from '@takeoff-ui/react-spar';
<Dialog>
<Dialog.Trigger />
<Dialog.Overlay />
<Dialog.Panel>
<Dialog.Header>
<Dialog.Title />
<Dialog.Close />
</Dialog.Header>
<Dialog.Body>
<Dialog.Description />
</Dialog.Body>
<Dialog.Footer />
</Dialog.Panel>
</Dialog>
Playground
Intensity
Use intensity to control how strong the backdrop appears behind the dialog.
Overlay Behavior
Use blur for a softened backdrop and invisible when the overlay should stay
interactive but visually disappear.
Header Type
Use headerType to switch the visual treatment of the dialog header without
changing the rest of the panel structure.
Dismissible
Set dismissible={false} when the dialog should stay open until the user
explicitly closes it.
Footer Type
Use footerType to switch the footer treatment without changing the action
layout.
Controlled
Use open and onOpenChange when the dialog state needs to be managed from
outside the component.
Accessibility & Keyboard
- The dialog renders with the proper
role,aria-labelledby, andaria-describedbywiring through Spar. - Focus is trapped inside the content while open and restored on close.
Dialog.Titleprovides the accessible label andDialog.Descriptionprovides the accessible description.Dialog.Bodyprovides the default content spacing area between header and footer.- When
dismissible={false}, Escape and outside interaction do not close the dialog.
| Key | Behavior |
|---|---|
| Escape | Closes the dialog unless dismissible={false}. |
| Tab / Shift+Tab | Cycles focus within the dialog content. |
API Reference
Dialog
Props
| Name | Type | Default | Description |
|---|---|---|---|
| children | React.ReactNode | - | Dialog parts rendered inside the root. |
| dismissible | boolean | true | Whether the dialog can be dismissed by clicking outside or pressing Escape. |
| id | string | - | Custom base ID for ARIA relationships. If not provided, one will be generated automatically. Sub-element IDs are derived as ${id}-title, ${id}-description, ${id}-content. |
| disabled | boolean | false | Disables all dialog triggers (prevents opening) |
| forceMount | boolean | false | Always render portal/overlay/content (for animation libraries) |
| open | boolean | - | Controlled open state |
| defaultOpen | boolean | false | Initial open state (uncontrolled) |
| modal | boolean | true | Whether dialog is modal (blocks interaction outside) |
Events
| Name | Type | Default | Description |
|---|---|---|---|
| onOpenChange | (open: boolean) => void | - | Callback when open state changes |
Dialog.Trigger
Data attributes
| Attribute | Applied when | Purpose |
|---|---|---|
| data-slot="root" | Always | Stable selector for wrapper styling on the root slot. |
Dialog.Overlay
Props
| Name | Type | Default | Description |
|---|---|---|---|
| invisible | boolean | false | When true, the overlay is rendered but visually invisible. |
| intensity | DialogOverlayIntensity | 'base' | Overlay backdrop intensity. |
| blur | boolean | false | Applies backdrop blur on the overlay. |
| classNames | Partial<Record<"root", string>> | - | |
| slotProps | Partial<Record<"root", React.HTMLAttributes<HTMLElement>>> | - | |
| container | HTMLElement | null | document.body | Portal container element. Content is portaled to document.body by default. |
Data attributes
| Attribute | Applied when | Purpose |
|---|---|---|
| data-slot="root" | Always | Stable selector for wrapper styling on the root slot. |
| data-state | Always | "open" or "closed" — used for overlay fade transitions. |
| data-intensity | Always | Reflects the resolved intensity prop. |
| data-invisible | When invisible is true | Indicates the overlay is visually hidden but still mounted. |
| data-blur | When blur is true | Enables backdrop blur styling on the overlay. |
Dialog.Panel
Props
| Name | Type | Default | Description |
|---|---|---|---|
| classNames | Partial<Record<"root", string>> | - | |
| slotProps | Partial<Record<"root", React.HTMLAttributes<HTMLElement>>> | - | |
| role | React.AriaRole | 'dialog' | ARIA role for dialog type |
| container | HTMLElement | null | document.body | Portal container element. Content is portaled to document.body by default. |
| trapFocus | boolean | true | Enable focus trapping |
| restoreFocus | boolean | true | Restore focus on close |
| initialFocus | HTMLElement | (() => HTMLElement) | - | Element to focus on open |
| finalFocus | HTMLElement | (() => HTMLElement) | - | Element to focus on close |
Events
| Name | Type | Default | Description |
|---|---|---|---|
| onOpenAutoFocus | (event: Event) => void | - | Callback before auto-focus |
| onCloseAutoFocus | (event: Event) => void | - | Callback before focus restore |
| onEscapeKeyDown | (event: KeyboardEvent) => void | - | Escape key handler |
| onPointerDownOutside | (event: PointerEvent) => void | - | Outside click handler |
| onInteractOutside | (event: PointerEvent) => void | - | Outside interaction handler with preventDefault capability |
Data attributes
| Attribute | Applied when | Purpose |
|---|---|---|
| data-slot="root" | Always | Stable selector for wrapper styling on the root slot. |
| data-state | Always | "open" or "closed" — forwarded from Spar dialog state. |
| data-modal | Always | Reflects whether the dialog is modal. |
| data-role | Always | Reflects the resolved ARIA role. |
Dialog.Header
Props
| Name | Type | Default | Description |
|---|---|---|---|
| headerType | DialogHeaderType | 'basic' | Type of the header. |
| classNames | Partial<Record<"root", string>> | - | |
| slotProps | Partial<Record<"root", React.HTMLAttributes<HTMLElement>>> | - |
Data attributes
| Attribute | Applied when | Purpose |
|---|---|---|
| data-slot="root" | Always | Stable selector for wrapper styling on the root slot. |
| data-header-type | Always | Reflects the resolved headerType prop. |
Dialog.Title
Props
| Name | Type | Default | Description |
|---|---|---|---|
| classNames | Partial<Record<"root", string>> | - | |
| slotProps | Partial<Record<"root", React.HTMLAttributes<HTMLElement>>> | - | |
| level | number | 5 | Heading level (1-6) |
Data attributes
| Attribute | Applied when | Purpose |
|---|---|---|
| data-slot="root" | Always | Stable selector for wrapper styling on the root slot. |
Dialog.Description
Data attributes
| Attribute | Applied when | Purpose |
|---|---|---|
| data-slot="root" | Always | Stable selector for wrapper styling on the root slot. |
Dialog.Body
Data attributes
| Attribute | Applied when | Purpose |
|---|---|---|
| data-slot="root" | Always | Stable selector for wrapper styling on the root slot. |
Dialog.Footer
Props
| Name | Type | Default | Description |
|---|---|---|---|
| footerType | DialogFooterType | 'basic' | Type of the footer. |
| classNames | Partial<Record<"root", string>> | - | |
| slotProps | Partial<Record<"root", React.HTMLAttributes<HTMLElement>>> | - |
Data attributes
| Attribute | Applied when | Purpose |
|---|---|---|
| data-slot="root" | Always | Stable selector for wrapper styling on the root slot. |
| data-footer-type | Always | Reflects the resolved footerType prop. |
Dialog.Close
Data attributes
| Attribute | Applied when | Purpose |
|---|---|---|
| data-slot="root" | Always | Stable selector for wrapper styling on the root slot. |
Type Definitions
| Name | Definition |
|---|---|
| DialogOverlayIntensity | 'lightest' | 'light' | 'base' | 'dark' | 'darkest' |
| DialogHeaderType | 'basic' | 'divided' | 'light' | 'dark' | 'primary' |
'basic' | 'divided' | 'light' |