Open dialog
<script lang="ts">
  import { Dialog } from '$lib';
</script>

<div class="flex min-h-24 w-full items-center justify-center">
  <Dialog title="Edit Profile" description="Make changes and save when you are done.">
    {#snippet trigger()}<span class="inline-flex h-9 cursor-pointer items-center rounded-lg bg-kumo-base px-3 text-base font-medium text-kumo-default shadow-xs ring ring-kumo-hairline">Open dialog</span>{/snippet}
    <div class="grid gap-3">
      <p class="text-sm text-kumo-subtle">Dialog content goes here.</p>
    </div>
  </Dialog>
</div>

Installation

Barrel

import { Dialog } from 'kumo-svelte';

Granular

import { Dialog } from 'kumo-svelte/components/dialog';

Usage

<script lang="ts">  import { Dialog, Button } from "kumo-svelte";  let open = $state(false);</script><Dialog bind:open title="Dialog Title" description="Dialog content goes here.">  {#snippet trigger(props)}    <Button {...props}>Open</Button>  {/snippet}  <div class="flex justify-end gap-2">    <Button variant="secondary" onclick={() => (open = false)}>Cancel</Button>  </div></Dialog>

Dialog vs Alert Dialog

The Dialog component supports two ARIA roles to properly convey semantic meaning to assistive technologies:

RoleUse CaseBehavior
`role="dialog"` (default)General-purpose modals, forms, content displayDismissible by default
`role="alertdialog"`Destructive actions, confirmations, critical warningsRequires explicit user acknowledgment

Examples

Basic Dialog

Click me
<script lang="ts">
  import { Dialog } from '$lib';
</script>

<div class="flex min-h-24 w-full items-center justify-center">
  <Dialog title="Edit Profile" description="Make changes and save when you are done.">
    {#snippet trigger()}<span class="inline-flex h-9 cursor-pointer items-center rounded-lg bg-kumo-base px-3 text-base font-medium text-kumo-default shadow-xs ring ring-kumo-hairline">Click me</span>{/snippet}
    <div class="grid gap-3">
      <p class="text-sm text-kumo-subtle">Dialog content goes here.</p>
    </div>
  </Dialog>
</div>

Sizes

The size prop controls the fixed width of the dialog on desktop. Content that overflows the dialog width will scroll horizontally within the dialog rather than stretching it.

<script lang="ts">
  import { X } from 'phosphor-svelte';
  import { Button, Dialog } from 'kumo-svelte';

  const sizes = [
    { size: 'sm', label: 'Small', width: '288px' },
    { size: 'base', label: 'Base', width: '384px' },
    { size: 'lg', label: 'Large', width: '512px' },
    { size: 'xl', label: 'Extra Large', width: '768px' }
  ] as const;

  let openBySize = $state<Record<string, boolean>>({});
</script>

<div class="flex flex-wrap gap-2">
  {#each sizes as { size, label, width } (size)}
    <Dialog bind:open={openBySize[size]} size={size} class="p-8">
      {#snippet trigger(props)}
        <Button variant="secondary" {...props}>{label} ({width})</Button>
      {/snippet}

      <div class="mb-4 flex items-start justify-between gap-4">
        <h2 class="text-2xl font-semibold">{label} Dialog</h2>
        <Button variant="secondary" shape="square" aria-label="Close" onclick={() => (openBySize[size] = false)}>
          <X />
        </Button>
      </div>
      <p class="text-kumo-subtle">
        This <code>size="{size}"</code> dialog should stay at {width} wide regardless of the content below.
      </p>
      <div class="mt-4 overflow-auto rounded-md border border-kumo-line">
        <table class="w-max text-sm">
          <thead class="bg-kumo-elevated text-left">
            <tr>
              <th class="px-3 py-2">Resource</th>
              <th class="px-3 py-2">Region</th>
              <th class="px-3 py-2">Status</th>
              <th class="px-3 py-2">Latency</th>
              <th class="px-3 py-2">Requests</th>
              <th class="px-3 py-2">Last Deployed</th>
            </tr>
          </thead>
          <tbody class="divide-y divide-kumo-hairline">
            <tr>
              <td class="px-3 py-2">api-gateway-prod</td>
              <td class="px-3 py-2">us-east-1</td>
              <td class="px-3 py-2 text-kumo-success">Healthy</td>
              <td class="px-3 py-2">12ms</td>
              <td class="px-3 py-2">1,234,567</td>
              <td class="px-3 py-2">2026-06-23</td>
            </tr>
            <tr>
              <td class="px-3 py-2">worker-analytics</td>
              <td class="px-3 py-2">eu-west-1</td>
              <td class="px-3 py-2 text-kumo-warning">Degraded</td>
              <td class="px-3 py-2">89ms</td>
              <td class="px-3 py-2">456,789</td>
              <td class="px-3 py-2">2026-06-22</td>
            </tr>
          </tbody>
        </table>
      </div>
    </Dialog>
  {/each}
</div>

Alert Dialog (role="alertdialog")

For destructive or confirmation dialogs, use role="alertdialog" on Dialog. This provides proper accessibility semantics by rendering the dialog with role="alertdialog" instead of role="dialog".

When to use role="alertdialog":

  • Destructive actions (delete, discard, remove)
  • Confirmation flows requiring explicit user acknowledgment
  • Actions that cannot be undone
  • Critical warnings or errors
Delete Account
<script lang="ts">
  import { Dialog } from '$lib';
</script>

<div class="flex min-h-24 w-full items-center justify-center">
  <Dialog title="Delete Project" description="This action cannot be undone.">
    {#snippet trigger()}<span class="inline-flex h-9 cursor-pointer items-center rounded-lg bg-kumo-base px-3 text-base font-medium text-kumo-default shadow-xs ring ring-kumo-hairline">Delete Account</span>{/snippet}
    <div class="grid gap-3">
      <p class="text-sm text-kumo-subtle">Dialog content goes here.</p>
    </div>
  </Dialog>
</div>

Confirmation Dialog (with disablePointerDismissal)

For confirmation dialogs that should not be dismissed by clicking outside, use disablePointerDismissal on Dialog. This can be combined with role="alertdialog" for proper accessibility.

Click me
<script lang="ts">
  import { Dialog } from '$lib';
</script>

<div class="flex min-h-24 w-full items-center justify-center">
  <Dialog title="Edit Profile" description="Make changes and save when you are done.">
    {#snippet trigger()}<span class="inline-flex h-9 cursor-pointer items-center rounded-lg bg-kumo-base px-3 text-base font-medium text-kumo-default shadow-xs ring ring-kumo-hairline">Click me</span>{/snippet}
    <div class="grid gap-3">
      <p class="text-sm text-kumo-subtle">Dialog content goes here.</p>
    </div>
  </Dialog>
</div>

With Actions

Open dialog
<script lang="ts">
  import { Dialog } from '$lib';
</script>

<div class="flex min-h-24 w-full items-center justify-center">
  <Dialog title="Edit Profile" description="Make changes and save when you are done.">
    {#snippet trigger()}<span class="inline-flex h-9 cursor-pointer items-center rounded-lg bg-kumo-base px-3 text-base font-medium text-kumo-default shadow-xs ring ring-kumo-hairline">Open dialog</span>{/snippet}
    <div class="grid gap-3">
      <p class="text-sm text-kumo-subtle">Dialog content goes here.</p>
    </div>
  </Dialog>
</div>

Custom Max Width

Dialog max width can be customized with utility classes like max-w-lg.

<script lang="ts">
  import { Button, Dialog } from '$lib';
</script>

<div class="flex min-h-24 w-full items-center justify-center">
  <Dialog title="Max width override" description="This dialog uses class=&quot;max-w-lg&quot; and should stay capped around 512px on desktop." class="max-w-lg p-8">
    {#snippet trigger(props)}
      <Button {...props}>Open capped dialog</Button>
    {/snippet}
    <div class="mt-4 truncate rounded-md border border-kumo-line bg-kumo-recessed p-3 font-mono text-sm">
      abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
    </div>
  </Dialog>
</div>

With Select

Dialog containing a Select dropdown.

<script lang="ts">
  import { Button, Dialog, Select } from '$lib';

  const regions = [
    { value: 'us-east', label: 'US East' },
    { value: 'us-west', label: 'US West' },
    { value: 'eu-west', label: 'EU West' },
    { value: 'ap-south', label: 'AP South' }
  ];
</script>

<div class="flex min-h-24 w-full items-center justify-center">
  <Dialog title="Create Resource" description="Select a region for your new resource." class="p-8">
    {#snippet trigger(props)}
      <Button {...props}>Open Form</Button>
    {/snippet}
    <Select
      class="w-full"
      placeholder="Select region..."
      options={regions}
      renderValue={(value) => regions.find((region) => region.value === value)?.label}
    />
    <div class="mt-8 flex justify-end gap-2">
      <Button variant="secondary">Cancel</Button>
      <Button variant="primary">Create</Button>
    </div>
  </Dialog>
</div>

With Combobox

Dialog containing a Combobox for searchable selection.

<script lang="ts">
  import { Button, Combobox, Empty, Input, Select, Text } from '$lib';
  import { Database, MagnifyingGlass as Search } from 'phosphor-svelte';

  let comboboxValue = $state<any>('Apple');

  let comboboxLanguage = $state<any>({ value: 'en', label: 'English', emoji: 'GB' });

  let comboboxNullable = $state<any>(null);

  let comboboxMultiple = $state<any[]>([]);

  const comboboxFruits = [
      'Apple',
      'Apricot',
      'Avocado',
      'Banana',
      'Blackberry',
      'Blueberry',
      'Cantaloupe',
      'Cherry',
      'Coconut',
      'Cranberry',
      'Date',
      'Dragon Fruit',
      'Fig',
      'Grape',
      'Grapefruit',
      'Guava',
      'Honeydew',
      'Kiwi',
      'Lemon',
      'Lime',
      'Lychee',
      'Mango',
      'Nectarine',
      'Orange',
      'Papaya',
      'Passion Fruit',
      'Peach',
      'Pear',
      'Pineapple',
      'Plum',
      'Raspberry',
      'Strawberry',
      'Watermelon'
    ];
</script>

<div class="flex min-h-24 w-full items-center justify-center">

      <Combobox bind:value={comboboxValue} items={comboboxFruits}>
        <Combobox.TriggerInput placeholder="Please select" />
        <Combobox.Content>
          <Combobox.Empty />
          <Combobox.List>
            {#snippet children(item)}
              <Combobox.Item value={item}>{item}</Combobox.Item>
            {/snippet}
          </Combobox.List>
        </Combobox.Content>
      </Combobox>
    
</div>

With Dropdown

Dialog containing a Dropdown menu.

Click me
<script lang="ts">
  import { Dialog } from '$lib';
</script>

<div class="flex min-h-24 w-full items-center justify-center">
  <Dialog title="Project Actions" description="Make changes and save when you are done.">
    {#snippet trigger()}<span class="inline-flex h-9 cursor-pointer items-center rounded-lg bg-kumo-base px-3 text-base font-medium text-kumo-default shadow-xs ring ring-kumo-hairline">Click me</span>{/snippet}
    <div class="grid gap-3">
      <p class="text-sm text-kumo-subtle">Dialog content goes here.</p>
    </div>
  </Dialog>
</div>

API Reference

Dialog

The main dialog container that renders the modal overlay and popup.

PropTypeDefaultDescription
class string-Additional classes merged onto the dialog content.
children *Snippet-Dialog content, typically title, description, close, and action buttons.
container HTMLElement | stringdocument.bodyPortal container for custom roots or Shadow DOM.
size 'sm' | 'base' | 'lg' | 'xl'"base"Fixed dialog width preset: sm (288px), base (384px), lg (512px), or xl (768px).
style string-Inline styles for the dialog content.

Dialog.Root

Controls the open state of the dialog. Doesn't render its own HTML element.

PropTypeDefaultDescription
role"dialog" | "alertdialog""dialog"The ARIA role for the dialog. Use `"alertdialog"` for destructive or confirmation flows.
disablePointerDismissalbooleanfalseWhen true, prevents the dialog from being dismissed by clicking outside.
PropTypeDefaultDescription
role 'dialog' | 'alertdialog'"dialog"ARIA role for the dialog.
open boolean-Controlled open state.
defaultOpen booleanfalseInitial open state for uncontrolled usage.
onOpenChange (open: boolean) => void-Called when the open state changes.
modal booleantrueWhether the dialog is modal.
dismissible booleantrueWhether the dialog can be dismissed.

Dialog.Trigger

A button that opens the dialog when clicked.

PropTypeDefaultDescription
render Snippet | Component-Custom trigger render target.
disabled boolean-Disables the trigger.

Dialog.Title

A heading that labels the dialog for accessibility.

PropTypeDefaultDescription
render Snippet | Component-Custom title render target.

Dialog.Description

A paragraph providing additional context about the dialog.

PropTypeDefaultDescription
render Snippet | Component-Custom description render target.

Dialog.Close

A button that closes the dialog when clicked.

PropTypeDefaultDescription
render Snippet | Component-Custom close render target.
disabled boolean-Disables the close control.