icon

Morainev0.5.0

Dialog
overlaysdialog

Dialog

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

Import#

import { Dialog } from 'moraine'

Slot Structure#

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

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.

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.

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.

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#

Slottrigger0 attributes
Element users activate to open the dialog.
No attribute metadata for this slot.

Props#

PropTypeDefaultDescription
bodyJSX.Element
Custom element to render in the body slot.
childrenJSX.Element
Content to render inside the dialog trigger slot.
classClassValue
Class applied to the component root or trigger element.
classesDialogT.Classes | undefined
Slot-based class overrides.
closeboolean | undefinedtrue
Whether to show a close button.
closeIconIconT.Name | JSX.Elementicon-close
Icon name or custom content for the close button.
defaultOpenboolean | undefined
Initial open state when uncontrolled.
descriptionJSX.Element
Secondary description displayed below the title.
dismissibleboolean | undefined
Whether outside interaction and Escape should dismiss the shell.
footerJSX.Element
Custom element to render in the footer slot.
fullscreenboolean | undefinedfalse
Whether the dialog should take up the full viewport.
headerJSX.Element
Custom element to render in the header slot.
idstring | 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.
openboolean | undefined
Controlled open state.
overlayboolean | undefined
Whether to render the overlay element.
refJSX.HTMLElementTags["span"] extends { ref?: infer Ref; } ? Ref : never | undefined
scrollableboolean | undefinedfalse
Whether the dialog content body should be scrollable.
styleJSX.CSSProperties | undefined
stylesDialogT.Styles | undefined
Slot-based style overrides.
titleJSX.Element
Primary title displayed in the dialog header.