Skip to content
Containment

Dialog

MD3 dialog, on the native <dialog> element.

Walkthrough

1. Basic

bind:open is the whole API, for a dialog with a body of your own — a short form finished or cancelled in one go. Escape and the backdrop close it.

Invite a teammate

They will get an email with a join link.

Anyone you invite can see every project in this workspace.

<script lang="ts">	import { Button, Dialog } from 'omaris';​	let open = $state(false);</script>​<Button onclick={() => (open = true)}>Invite a teammate</Button>​<Dialog bind:open title="Invite a teammate" description="They will get an email with a join link.">	<p class="text-body-md text-muted-foreground">		Anyone you invite can see every project in this workspace.	</p>	{#snippet actions()}		<Button variant="text" onclick={() => (open = false)}>Cancel</Button>		<Button onclick={() => (open = false)}>Send invite</Button>	{/snippet}</Dialog>

2. Promise dialogs

dialog.confirm() returns a promise, so a confirmation reads as one line instead of a state variable and two callbacks. OmarisProvider renders the host these appear in.

<script lang="ts">	import { Button, dialog, toast } from 'omaris';​	async function remove() {		const ok = await dialog.confirm({			title: 'Delete this project?',			description: 'Everything in it goes too. This cannot be undone.',			confirmText: 'Delete',			destructive: true		});		if (ok) toast.success('Project deleted');	}​	async function rename() {		const name = await dialog.prompt({ title: 'Rename project', value: 'storefront' });		if (name) toast('Renamed to ' + name);	}</script>​<Button variant="tonal" tone="destructive" onclick={remove}>Delete…</Button><Button variant="tonal" onclick={rename}>Rename…</Button>

3. Sizes

Five widths. full fills the screen, which is right for an editor.

size = sm

Below 640px every size becomes a bottom sheet instead, unless you turn responsive off.

<script lang="ts">	import { Button, Dialog } from 'omaris';​	let size = $state<'sm' | 'md' | 'lg' | 'xl' | 'full'>('sm');	let open = $state(false);​	function show(next: typeof size) {		size = next;		open = true;	}</script>​<Button variant="outlined" size="xs" onclick={() => show('sm')}>sm</Button><Button variant="outlined" size="xs" onclick={() => show('md')}>md</Button><Button variant="outlined" size="xs" onclick={() => show('lg')}>lg</Button><Button variant="outlined" size="xs" onclick={() => show('xl')}>xl</Button><Button variant="outlined" size="xs" onclick={() => show('full')}>full</Button>​<Dialog bind:open {size} title="size = {size}">	<p class="text-body-md text-muted-foreground">		Below 640px every size becomes a bottom sheet instead, unless you turn		<code>responsive</code> off.	</p></Dialog>

4. Morphing

The panel resizes itself as its content changes rather than jumping, which is what makes a multi-step dialog feel like one surface.

Step 1 of 3

Line 1 of this step's content.

Line 2 of this step's content.

<script lang="ts">	import { Button, Dialog } from 'omaris';​	let open = $state(false);	let step = $state(1);</script>​<Button	onclick={() => {		step = 1;		open = true;	}}>	Open a three-step dialog</Button>​<Dialog bind:open title="Step {step} of 3">	<div class="flex flex-col gap-3">		{#each { length: step * 2 }, i (i)}			<p class="text-body-md text-muted-foreground">Line {i + 1} of this step's content.</p>		{/each}	</div>	{#snippet actions()}		<Button variant="text" disabled={step === 1} onclick={() => step--}>Back</Button>		{#if step < 3}			<Button onclick={() => step++}>Next</Button>		{:else}			<Button onclick={() => (open = false)}>Done</Button>		{/if}	{/snippet}</Dialog>

5. Right to left

Nothing in the dialog is positioned in left/right terms, so dir="rtl" mirrors the whole thing: the headline, the close button in the top corner, and the actions along the bottom.

دعوة زميل إلى مساحة العمل

سيصلهم بريد فيه رابط للانضمام.

يمكن لمن تدعوه رؤية كل المشاريع هنا.

<script lang="ts">	import { Button, Dialog } from 'omaris';​	let open = $state(false);</script>​<div dir="rtl">	<Button onclick={() => (open = true)}>دعوة زميل</Button></div>​<Dialog	bind:open	dir="rtl"	title="دعوة زميل إلى مساحة العمل"	description="سيصلهم بريد فيه رابط للانضمام.">	<p class="text-body-md text-muted-foreground">يمكن لمن تدعوه رؤية كل المشاريع هنا.</p>	{#snippet actions()}		<Button variant="text" onclick={() => (open = false)}>إلغاء</Button>		<Button onclick={() => (open = false)}>إرسال الدعوة</Button>	{/snippet}</Dialog>

6. Overridden

closable={false} and dismissible={false} make a dialog you have to answer — use it sparingly, and only when leaving would lose work.

You have unsaved changes

Leaving now discards them.

<script lang="ts">	import { Button, Dialog } from 'omaris';​	let open = $state(false);</script>​<Button variant="outlined" onclick={() => (open = true)}>Unsaved changes</Button>​<Dialog	bind:open	title="You have unsaved changes"	closable={false}	dismissible={false}	responsive={false}	classes={{ panel: 'border-2 border-warning' }}>	<p class="text-body-md text-muted-foreground">Leaving now discards them.</p>	{#snippet actions()}		<Button variant="text" tone="destructive" onclick={() => (open = false)}>Discard</Button>		<Button onclick={() => (open = false)}>Keep editing</Button>	{/snippet}</Dialog>