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.
<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.
<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.
<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.
<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>