<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:
| Role | Use Case | Behavior |
|---|---|---|
| `role="dialog"` (default) | General-purpose modals, forms, content display | Dismissible by default |
| `role="alertdialog"` | Destructive actions, confirmations, critical warnings | Requires explicit user acknowledgment |
Examples
Basic 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">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
<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.
<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
<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="max-w-lg" 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.
<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.
| Prop | Type | Default | Description |
|---|---|---|---|
| class | string | - | Additional classes merged onto the dialog content. |
| children * | Snippet | - | Dialog content, typically title, description, close, and action buttons. |
| container | HTMLElement | string | document.body | Portal 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.
| Prop | Type | Default | Description |
|---|---|---|---|
| role | "dialog" | "alertdialog" | "dialog" | The ARIA role for the dialog. Use `"alertdialog"` for destructive or confirmation flows. |
| disablePointerDismissal | boolean | false | When true, prevents the dialog from being dismissed by clicking outside. |
| Prop | Type | Default | Description |
|---|---|---|---|
| role | 'dialog' | 'alertdialog' | "dialog" | ARIA role for the dialog. |
| open | boolean | - | Controlled open state. |
| defaultOpen | boolean | false | Initial open state for uncontrolled usage. |
| onOpenChange | (open: boolean) => void | - | Called when the open state changes. |
| modal | boolean | true | Whether the dialog is modal. |
| dismissible | boolean | true | Whether the dialog can be dismissed. |
Dialog.Trigger
A button that opens the dialog when clicked.
| Prop | Type | Default | Description |
|---|---|---|---|
| render | Snippet | Component | - | Custom trigger render target. |
| disabled | boolean | - | Disables the trigger. |
Dialog.Title
A heading that labels the dialog for accessibility.
| Prop | Type | Default | Description |
|---|---|---|---|
| render | Snippet | Component | - | Custom title render target. |
Dialog.Description
A paragraph providing additional context about the dialog.
| Prop | Type | Default | Description |
|---|---|---|---|
| render | Snippet | Component | - | Custom description render target. |
Dialog.Close
A button that closes the dialog when clicked.
| Prop | Type | Default | Description |
|---|---|---|---|
| render | Snippet | Component | - | Custom close render target. |
| disabled | boolean | - | Disables the close control. |