Modal
Import
Section titled “Import”import { Modal } from '@delightstack/components';Basic Usage
Section titled “Basic Usage”View code
<script> import { Modal, Button } from '@delightstack/components'; let open = $state(false);</script>
<Button onclick={() => open = true}>Open Modal</Button>
<Modal bind:open title="Example Modal"> <p>This is a modal dialog with some example content. Click outside or press Escape to close.</p></Modal>Examples
Section titled “Examples”With Footer Actions
Section titled “With Footer Actions”View code
<Modal bind:open title="Edit Profile"> <p>Update your profile information below.</p>
{#snippet footer()} <Button transparent onclick={() => open = false}>Cancel</Button> <Button accent onclick={() => open = false}>Save Changes</Button> {/snippet}</Modal>Custom Header
Section titled “Custom Header”Replace the entire header with a custom snippet.
View code
<Modal bind:open> {#snippet header()} <div style="display: flex; flex-direction: column; gap: 0.25rem;"> <h2 style="margin: 0; font-size: 1.25rem;">Settings</h2> <span style="font-size: 0.85rem; opacity: 0.7;">Configure your preferences</span> </div> {/snippet}
<p>Settings content goes here.</p></Modal>Header Start and End Slots
Section titled “Header Start and End Slots”Add content before the title or after it (before the close button).
View code
<Modal bind:open title="Document"> {#snippet header_start()} <span class="status-badge">Draft</span> {/snippet}
{#snippet header_end()} <Button transparent dense icon aria-label="Share"> <ShareIcon /> </Button> {/snippet}
<p>Document content...</p></Modal>Custom Width and Height
Section titled “Custom Width and Height”Override the default sizing with explicit CSS values.
View code
<Modal bind:open title="Wide Modal" width="800px" height="500px"> <p>This modal has a custom size of 800x500px.</p></Modal>Non-Closable Modal
Section titled “Non-Closable Modal”Prevent closing via Escape key and backdrop click for required flows.
View code
<Modal bind:open title="Required Step" closable={false}> <p>You must complete this step before continuing.</p>
{#snippet footer()} <Button accent onclick={() => open = false}>Done</Button> {/snippet}</Modal>Transition from Element
Section titled “Transition from Element”Animate the modal from a specific trigger element for a connected spatial experience.
<script> import { Modal, Button } from '@delightstack/components';
let showModal = $state(false); let triggerEl = $state<HTMLElement>();</script>
<Button bind:element={triggerEl} onclick={() => showModal = true}> Open</Button>
<Modal bind:open={showModal} title="Hero Transition" transition_target={triggerEl}> <p>This modal animated from the button.</p></Modal>| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Controls visibility ($bindable()) |
title | string | '' | Modal title text |
closable | boolean | true | Allow closing via Escape key and backdrop click |
disable_close_icon | boolean | false | Hide the X close button |
modal_id | string | '' | ID used for transition target tracking |
width | string | '' | Explicit CSS width (desktop) |
height | string | '' | Explicit CSS height (desktop) |
max_width | string | 'calc(100vw - 2rem)' | Maximum width |
max_height | string | 'calc(100svh - 2rem)' | Maximum height |
transition_target | HTMLElement | Element | undefined | Element to animate from when opening |
style | string | '' | Inline CSS styles |
class | string | '' | Additional CSS classes |
children | Snippet | undefined | Main modal content |
header | Snippet | undefined | Replace entire header |
header_start | Snippet | undefined | Content before the title |
header_end | Snippet | undefined | Content after the title (before close button) |
footer | Snippet | undefined | Footer content area |
footer_start | Snippet | undefined | Left side of footer |
footer_end | Snippet | undefined | Right side of footer |
onopen | () => void | undefined | Called when the modal opens |
onclose | () => boolean | undefined | void | undefined | Called when the modal closes. Return false to prevent closing. |
onbackdropclick | () => void | undefined | Called when the backdrop is clicked |
Events
Section titled “Events”| Event | Detail | Description |
|---|---|---|
onopen | none | Fires when the modal mounts |
onclose | none | Fires when the modal is being closed. Return false to prevent it. |
onbackdropclick | none | Fires when the backdrop overlay is clicked |
Accessibility
Section titled “Accessibility”role="dialog"witharia-modal="true"aria-labelledbypointing to the title elementaria-describedbypointing to the body content- Focus trap via
focusTrapfrom@delightstack/utilities(Tab cycles within modal only) - Escape key dismissal (when
closableis true) - Body scroll is locked while the modal is open (
html { overflow: hidden }) - Backdrop has a blur effect for visual depth