motif svelteArc

Dialog ported

A focused surface for decisions that need attention.

A modal overlay and panel with a titled header, built-in close button, accessible focus trap, and interruptible spring transitions.

Live specimen

Loading demo…

Guidance

When to use

  • Confirmations and decisions that must interrupt, such as Delete project.
  • Short forms like rename or invite that fit in one focused panel.
  • Flows where the dialog title changes between steps and should crossfade in place.

When not to use

  • Use Drawer for long forms or detail panels that keep the page in context.
  • Use Bottom Sheet for mobile-first secondary tasks with snap heights.
  • Use Popover for light, non-modal content anchored to a trigger.

Installation

pnpm dlx shadcn-svelte@latest add @arcui/dialog

Usage

<script lang="ts">
  import { Dialog, DialogTrigger, DialogContent, DialogClose } from '$lib/components/dialog';
</script>

<Dialog>
  <DialogTrigger class="btn-primary">Rename project</DialogTrigger>
  <DialogContent title="Rename project" description="This updates the project URL across your team.">
    <div class="form-body">
      <label for="name">Project Name</label>
      <input id="name" type="text" value="Arc UI Svelte" />
    </div>
    <div class="dialog-actions">
      <DialogClose class="btn-secondary">Cancel</DialogClose>
      <DialogClose class="btn-primary">Save Changes</DialogClose>
    </div>
  </DialogContent>
</Dialog>

Variants & Examples

Standard Modal Dialog

Uncontrolled dialog triggered by a nested button with title and description.

<Dialog>
  <DialogTrigger>Open Dialog</DialogTrigger>
  <DialogContent title="Quick Action" description="Perform an immediate action.">
    <p>Dialog body content.</p>
  </DialogContent>
</Dialog>

Controlled Two-Way Binding

Controlled visibility using Svelte 5 bind:open rune.

<script lang="ts">
  let isModalOpen = $state(false);
</script>

<Dialog bind:open={isModalOpen}>
  <DialogTrigger>Configure</DialogTrigger>
  <DialogContent title="Configuration">
    <p>Settings panel</p>
  </DialogContent>
</Dialog>

Destructive Confirmation

Dialog focused on irreversible actions with clear cancel and confirm paths.

<Dialog>
  <DialogTrigger class="btn-danger">Delete</DialogTrigger>
  <DialogContent title="Delete Project?" description="This action cannot be undone.">
    <div class="actions">
      <DialogClose>Cancel</DialogClose>
      <DialogClose class="btn-danger">Confirm Delete</DialogClose>
    </div>
  </DialogContent>
</Dialog>

API Reference

PropTypeDefaultDescription
titlestring—Dialog title, rendered as Dialog.Title. In Phase 2, title changes crossfade in place.
descriptionstring—Optional supporting line, rendered as Dialog.Description.
openboolean—Controlled open state. Supports two-way bind:open.
defaultOpenbooleanfalseInitial open state when uncontrolled.
onOpenChange(open: boolean) => void—Called when the dialog opens or closes.
onInteractOutside(event: PointerEvent) => void—Callback when clicking outside the dialog content.
childrenSnippet—Modal body content rendered below header and above close button.
refHTMLElement | null—Bindable ref to the underlying Dialog.Content element.

Keyboard Interactions

KeyAction
EscapeCloses the dialog and returns focus cleanly to the trigger.
Tab / Shift+TabCycles focus trapped strictly within the active dialog modal.

Accessibility

  • Bits UI renders role="dialog" with aria-modal="true", traps focus, and restores it cleanly to the trigger on close.
  • title and description automatically link to aria-labelledby and aria-describedby.
  • The built-in close button carries aria-label="Close dialog".

Motion

Phase 1 still-state: overlay and panel render their static rest and leave end-states with visibility toggles. Phase 2 springs the overlay fade and panel 8px rise / 0.96 scale with interruptible retargeting. Reduced motion uses a plain opacity fade.

Notes for AI

  • Use for decisions that must interrupt: confirmations, short forms. Use Drawer for side panels, Bottom Sheet for mobile-first secondary tasks, Popover for light non-modal content.
  • Always compose Dialog with DialogTrigger, DialogContent, and optional DialogClose.

Source of truth: registry/components/dialog/dialog.tsx