---
title: Dialog
description: Modal dialog with structured slots, backdrop overlay, and dismissal control.
sidebar:
  order: 4
search:
  tags: [modal, overlay, confirmation, focus trap]
---

# Dialog

> Modal dialog with structured slots, backdrop overlay, and dismissal control.

## Import

```tsx
import { Dialog } from 'moraine'
```

## Slot Structure

Wrapper trigger with optional backdrop and a floating panel with title, body, and footer slots.

```text
trigger
├── overlay (optional)
└── content (portal)
    ├── header (optional)
    │   ├── wrapper (optional)
    │   │   ├── title (optional)
    │   │   └── description (optional)
    │   └── close (optional)
    ├── body (optional)
    └── footer (optional)
```

## Examples

### Default Shell

Header, description, actions, body, and footer slots.

```tsx
function DefaultShell() {
  return (
    <div class="flex flex-wrap gap-3 items-center">
      <Dialog
        title="Delete Project"
        description="This action cannot be undone."
        body={
          <p class="text-sm text-foreground">
            The selected project and all related records will be permanently removed.
          </p>
        }
        footer={
          <>
            <Button variant="outline">Cancel</Button>
            <Button variant="destructive">Delete</Button>
          </>
        }
      >
        <Button>Open dialog</Button>
      </Dialog>
    </div>
  )
}
```

### Variants

`close` supports default button, hidden, and custom JSX content.

```tsx
function Variants() {
  return (
    <div class="flex flex-wrap gap-3 items-center">
      <Dialog
        title="No close button"
        close={false}
        body="This dialog has no top-right close button."
      >
        <Button variant="outline">Close=false</Button>
      </Dialog>

      <Dialog
        title="Custom close"
        closeIcon={<span class="text-xs font-semibold size-full">Done</span>}
        body="Custom close content rendered in the close button."
      >
        <Button variant="outline">Custom close</Button>
      </Dialog>
    </div>
  )
}
```

### Scrollable + Dismissible Control

Scrollable body with prevent-close callback when dismiss is disabled.

```tsx
function ScrollableDismissibleControl() {
  const SCROLLABLE_LINES = Array.from(
    { length: 16 },
    (_, index) => `Release note line ${index + 1}`,
  )

  const [preventedCloseCount, setPreventedCloseCount] = createSignal(0)

  return (
    <div class="flex flex-wrap gap-3 items-center">
      <Dialog
        scrollable
        title="Release Notes"
        description="Scrollable dialog content."
        body={
          <div class="space-y-1">
            <For each={SCROLLABLE_LINES}>
              {(line) => <p class="text-sm text-foreground">{line}</p>}
            </For>
          </div>
        }
      >
        <Button variant="secondary">Scrollable dialog</Button>
      </Dialog>

      <Dialog
        defaultOpen
        dismissible={false}
        onClosePrevent={() => setPreventedCloseCount((value) => value + 1)}
        title="Persistent dialog"
        body={
          <p class="text-sm text-foreground">
            Prevented close attempts: <span class="font-medium">{preventedCloseCount()}</span>
          </p>
        }
      >
        <Button variant="outline">Dismiss blocked</Button>
      </Dialog>
    </div>
  )
}
```

## API Reference

### Attributes

#### `trigger`

Element users activate to open the dialog.

#### `overlay`

Backdrop layer rendered behind the dialog panel.

#### `content`

Dialog panel containing header, body, footer, and close control.

##### ARIA Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| aria-describedby | boolean \| string \| undefined | References descriptive text associated with the control. |
| aria-labelledby | boolean \| string \| undefined | References the element that labels the control or region. |
| aria-modal | boolean \| string \| undefined | Identifies modal content that traps interaction outside the dialog. |
| role | string | Defines the semantic role exposed to assistive technology. |

#### `header`

Top region for dialog title and description.

#### `wrapper`

Inner card wrapper that arranges dialog header, body, and footer.

#### `title`

Accessible title for the dialog.

#### `description`

Supporting text associated with the dialog title.

#### `close`

Button that dismisses the dialog.

##### ARIA Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| aria-label | boolean \| string \| undefined | Provides an accessible label when visible text is not sufficient. |

#### `body`

Main dialog content region.

#### `footer`

Bottom region for dialog actions.

### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| body | JSX.Element | — | Custom element to render in the body slot. |
| children | JSX.Element | — | Content to render inside the dialog trigger slot. |
| class | ClassValue | — | Class applied to the component root or trigger element. |
| classes | DialogT.Classes \| undefined | — | Slot-based class overrides. |
| close | boolean \| undefined | true | Whether to show a close button. |
| closeIcon | IconT.Name \| JSX.Element | icon-close | Icon name or custom content for the close button. |
| defaultOpen | boolean \| undefined | — | Initial open state when uncontrolled. |
| description | JSX.Element | — | Secondary description displayed below the title. |
| dismissible | boolean \| undefined | — | Whether outside interaction and Escape should dismiss the shell. |
| footer | JSX.Element | — | Custom element to render in the footer slot. |
| fullscreen | boolean \| undefined | false | Whether the dialog should take up the full viewport. |
| header | JSX.Element | — | Custom element to render in the header slot. |
| id | string \| undefined | — | Unique identifier used to derive the content id. |
| layout | "default" \| "scrollable" \| "fullscreen" \| undefined | — | — |
| onClosePrevent | (() => void) \| undefined | — | Called when a dismissal attempt is blocked. |
| onOpenChange | ((open: boolean) => void) \| undefined | — | Called whenever the open state changes. |
| open | boolean \| undefined | — | Controlled open state. |
| overlay | boolean \| undefined | — | Whether to render the overlay element. |
| ref | JSX.HTMLElementTags["span"] extends { ref?: infer Ref; } ? Ref : never \| undefined | — | — |
| scrollable | boolean \| undefined | false | Whether the dialog content body should be scrollable. |
| style | JSX.CSSProperties \| undefined | — | — |
| styles | DialogT.Styles \| undefined | — | Slot-based style overrides. |
| title | JSX.Element | — | Primary title displayed in the dialog header. |

### ARIA

Accessibility attributes and roles emitted by the component markup.

| Attribute | Type | Description |
| --- | --- | --- |
| aria-describedby | boolean \| string \| undefined | References descriptive text associated with the control. |
| aria-hidden | boolean \| string \| undefined | Hides decorative content from assistive technology. |
| aria-label | boolean \| string \| undefined | Provides an accessible label when visible text is not sufficient. |
| aria-labelledby | boolean \| string \| undefined | References the element that labels the control or region. |
| aria-modal | boolean \| string \| undefined | Identifies modal content that traps interaction outside the dialog. |
| role | string | Defines the semantic role exposed to assistive technology. |

### Data Attributes

State and slot attributes exposed for styling hooks and selectors.

| Attribute | Type | Description |
| --- | --- | --- |
| data-slot | string | Identifies the rendered slot for styling hooks and selectors. |
