GitHub repository

Modal

Accessible overlay dialog built on native <dialog>.

A modal dialog built on the native `<dialog>` element, so the platform supplies the focus trap, Esc-to-close and an inert backdrop. Bind `open` to your state; the dialog opens/closes in lock-step. Optional `header`/`footer` snippets frame the body, `closeOnBackdrop`/`closeOnEsc` tune dismissal, and `danger` flags destructive dialogs. Sets `aria-labelledby` to the `title` automatically, or accepts an `aria-label` when untitled.

Demo

live · storybook Open in Storybook

Demo loads from https://dssoca-storybook.vercel.app/. Run pnpm storybook for it to render locally.

Usage

svelte
<script>
  import { Modal, Button } from 'dssoca';
  let open = $state(false);
</script>

<Button onclick={() => (open = true)}>Open</Button>
<Modal bind:open title="Confirm action">
  {#snippet footer()}
    <Button variant="ghost" onclick={() => (open = false)}>Cancel</Button>
    <Button onclick={() => (open = false)}>Confirm</Button>
  {/snippet}
  <p>Are you sure you want to continue?</p>
</Modal>

<!-- Fills the viewport: header/footer pinned, body scrolls, size axis inert -->
<Modal bind:open={logsOpen} fullscreen title="Log stream">
  <LogStream {lines} />
</Modal>

Uses the native `<dialog>` element: the browser provides the focus trap, Esc handling and an inert `::backdrop`. `aria-labelledby` points at the title when set; otherwise pass `aria-label`. The close button carries `aria-label="Close"`. Size controls the dialog max-width only. Zero border-radius. With `fullscreen` the dialog fills the viewport (`100dvh`, `vh` fallback) and the size axis has no effect on width; because the panel is full-bleed there is no backdrop area left to hit, so `closeOnBackdrop` is unreachable in that mode — the close button and Esc are the exits. Responsiveness stays with the caller: pass `fullscreen={width < 640}` to mirror MUI's full-screen-on-mobile pattern.

HTML

interactive with vanilla.js The exact markup the component renders, generated at build time. Use it with dssoca/vanilla.css+ dssoca/vanilla.js — see Plain HTML & CSS.

html
<button class="ss-btn secondary" type="button" data-ss-modal="#confirm">open</button>
<dialog id="confirm" class="ss-modal" aria-labelledby="modal-s1-title">
  <div class="panel">
    <header class="head">
      <h2 class="title" id="modal-s1-title">Delete item</h2>
      <button type="button" class="close" data-ss-dismiss aria-label="Close">×</button>
    </header>
    <div class="body">
      <p>This cannot be undone.</p>
    </div>
    <footer class="foot">
      <button class="ss-btn secondary" type="button" data-ss-dismiss>cancel</button>
      <button class="ss-btn danger" type="button" data-ss-dismiss>delete</button>
    </footer>
  </div>
</dialog>

A native <dialog>. vanilla.js opens it from any [data-ss-modal="#id"] element and closes it from [data-ss-dismiss] or a backdrop click (add data-ss-static to keep it open; data-ss-no-esc blocks Escape).

Props

PropTypeDefaultDescription
openbooleanfalseBindable. Whether the modal is shown; syncs to the native dialog.
titlestring—Header title; wires `aria-labelledby`. Omit and pass `aria-label` for an untitled dialog.
closeOnBackdropbooleantrueClicking outside the panel (on the backdrop) closes the modal.
closeOnEscbooleantrueEsc closes the modal. When false the cancel event is prevented.
dangerbooleanfalseStyling hint for destructive dialogs (tints the title).
fullscreenbooleanfalseFill the viewport instead of a centred, capped panel: header and footer stay pinned, the body scrolls, the size axis goes inert and the backdrop is no longer clickable.
aria-labelstring—Accessible name used when no `title` is set.
onclose() => void—Called after the dialog closes (Esc, backdrop, or button).
headerSnippet—Replaces the default title row in the header.
footerSnippet—Footer content, typically a trailing cancel→confirm action pair (the affirmative `Button` trails the dismissive `ghost` one, per dialog convention).
childrenSnippet—Required. The modal body content.
size'sm' | 'md' | 'lg'—Per-instance size override; inherits the ancestor `data-size-variant` when unset.
↑↓ navigate · ↵ open · esc close

Keyboard shortcuts

No shortcuts registered

Shortcuts added through the dssoca registry will show up here.

Single-key shortcuts