Components / Dialog

Dialog

Accessible modal built on native <dialog>. Focus trap, escape-to-close, click-outside-to-close. Open imperatively via window.YPAIDialog.open(id).

Section: Components

Examples

Default (md)

Confirm action

This is a default md dialog. It is centered, has a backdrop blur, and its first focusable element receives focus on open. Hit Esc, click the X, or click the backdrop to dismiss.

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

Quick prompt

Tight 400px width — ideal for yes/no confirmations.

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

Edit configuration

Large 820px width for content-heavy modals: forms with multiple fields, terms of service excerpts, embedded media previews.

Body scrolls when content overflows; header and action row stay pinned.

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

Detailed view

Near-fullscreen (96vw / 92vh, capped at 1200px). Use for command palettes, media galleries, multi-step flows.

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)

No title prop = no header rendered. No actions slot used = no footer.

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> with showModal() — focus trap, scrim, and inert background are inherited.
  • The primitive sets aria-modal="true" on open and exposes the title via aria-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"> with aria-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>