Examples
Default (md)
usage.astro | astro
<button type="button" onclick="window.YPAIDialog.open('demo-md')">
Open default dialog
</button>
<Dialog id="demo-md" title="Confirm action">
<p>This is a default md dialog. Esc / X / backdrop click all dismiss.</p>
<div slot="actions">
<button onclick="window.YPAIDialog.close('demo-md')">Cancel</button>
<button onclick="window.YPAIDialog.close('demo-md')">Confirm</button>
</div>
</Dialog> Small
usage.astro | astro
<Dialog id="demo-sm" title="Quick prompt" size="sm">
<p>Tight 400px width — ideal for yes/no confirmations.</p>
<div slot="actions">
<button onclick="window.YPAIDialog.close('demo-sm')">OK</button>
</div>
</Dialog> Large
usage.astro | astro
<Dialog id="demo-lg" title="Edit configuration" size="lg">
<p>Large 820px width for content-heavy modals.</p>
<div slot="actions">
<button onclick="window.YPAIDialog.close('demo-lg')">Discard</button>
<button onclick="window.YPAIDialog.close('demo-lg')">Save changes</button>
</div>
</Dialog> Full-bleed
usage.astro | astro
<Dialog id="demo-full" title="Detailed view" size="full">
<p>Near-fullscreen (96vw / 92vh, capped at 1200px).</p>
<div slot="actions">
<button onclick="window.YPAIDialog.close('demo-full')">Done</button>
</div>
</Dialog> Untitled (no header, no actions)
usage.astro | astro
<Dialog id="demo-bare">
<p>No title prop = no header rendered. No actions slot used = no footer.</p>
<button onclick="window.YPAIDialog.close('demo-bare')">Close</button>
</Dialog> Anatomy
| Property | Behavior |
|---|---|
| Backdrop | Native `::backdrop` pseudo-element on `<dialog>`. Blurred + dimmed. |
| Surface | The dialog box itself. `--ds-color-bg-modal`, `--ds-shadow-5`, sized by `size` prop. |
| Header | Optional — renders when `title` is set. Includes the close (X) button. |
| Body | Slot content. Scrolls when overflowing; header and actions stay pinned. |
| Actions | Optional footer row. Renders only when `slot="actions"` is supplied. |
Accessibility
- Built on native
<dialog>withshowModal()— focus trap, scrim, and inert background are inherited. - The primitive sets
aria-modal="true"on open and exposes the title viaaria-labelledby. - Focus moves to the first focusable element in the dialog body on open, and is restored to the trigger on close.
- Escape always closes — no exceptions, no opt-out (per APG dialog spec).
- The close button is a real
<button type="button">witharia-label="Close dialog". - Nesting modals is supported but discouraged; the primitive ensures focus returns to the previous trigger on each close.
Do / Don't
| Property | Do | Don't |
|---|---|---|
| Focus management | Let the primitive trap focus inside the dialog body. | Render nested dialogs without a return-focus plan — focus restore breaks. |
| Backdrop behavior | Default click-outside-to-close is fine for non-destructive flows. | Use click-outside for delete confirmations — require an explicit cancel button. |
| Title | Pass a short `title` (under 60 chars) describing what the user is about to do. | Skip the title — SR users lose context for the modal. |
| Content height | Let body content scroll; keep header and footer pinned. | Cram a 2000px form into a dialog — promote it to a full page. |
| CTA emphasis | One primary action (e.g. "Save"), one secondary (e.g. "Cancel"). | Show 3+ primary buttons — pick the safest as default. |
Props
| Property | Type | Default |
|---|---|---|
| id | string | required |
| title | string | - |
| size | sm | md | lg | full | 'md' |
Slots
| Property | Purpose |
|---|---|
| default | Dialog body content. |
| actions | Footer button row, right-aligned. Renders only if used. |
Imperative API
| Property | Behavior |
|---|---|
| YPAIDialog.open(id) | Opens the dialog with the given id (calls showModal). |
| YPAIDialog.close(id) | Closes the dialog. |
| YPAIDialog.isOpen(id) | Returns true if the dialog is currently open. |
Usage
my-page.astro | astro
---
import Dialog from '@/components/ui/Dialog.astro';
---
<button type="button" onclick="window.YPAIDialog.open('confirm-delete')">
Delete
</button>
<Dialog id="confirm-delete" title="Delete record?" size="sm">
<p>This action cannot be undone.</p>
<div slot="actions">
<button type="button" onclick="window.YPAIDialog.close('confirm-delete')">
Cancel
</button>
<button type="button" class="danger">Delete</button>
</div>
</Dialog>