Dialog

Dialog is a pure modal shell — it owns only the mechanics (portal to <body>, scroll-lock, backdrop, dismiss, focus-trap, ARIA) and renders whatever you put inside. For the common title + message + Confirm/Cancel pattern, reach for ConfirmDialog, which composes a Card on top of the shell.

ConfirmDialog — the common case

ConfirmDialog — custom body

Pass children to replace the message with your own content; the header and footer stay.

Dialog — your own layout

Use Dialog directly to control the whole panel — e.g. your own Card with a separator-less footer, or raw content with no Card at all.

Sizing — width / height

Both accept any CSS value — %, vw/vh, px, rem — and are capped to the viewport minus a 1rem inset so the panel never runs off-screen. Omit either for content-sizing.

Controlling focus — onShow / onHide

By default the shell focuses the first focusable element (here, the input). onShow(ctx) hands you ctx.autoFocus (preset to the default selector) — retarget it to a selector or element, or set false to skip. Below, onShow focuses the Close button instead of the input. (onHide(ctx) mirrors it with ctx.restoreFocus.)

What the shell handles

  • Top layer: portaled to <body> — never clipped by an ancestor and never perturbs page layout.
  • Scroll lock: background scroll is locked while open (scrollbar-width compensated — no sideways jump).
  • Dismiss: backdrop-click + Escape close it; dismissible={false} for a must-decide modal.
  • Focus: focus moves into the panel on open, is trapped (Tab wraps), and returns to the trigger on close — all overridable via onShow/onHide.
  • Accessible: role="dialog" + aria-modal; pass aria-label / aria-labelledby through to the panel.