Component
<dom-dialog>
custom elementSelf-contained wrapper around the native HTML <dialog>. Top-layer rendering, nested stacking, ::backdrop and Esc-to-close are all native. Trigger slot is optional — open via commandfor, the open attribute, or .open() / .close() / .toggle().
Demo
Confirm dialog
Trigger via slot="trigger" — native <dialog> top layer, backdrop dismiss, and data-close buttons.
basic.htmlhtml
<dom-dialog>
<button
slot="trigger"
type="button"
class="inline-flex h-10 items-center rounded-full bg-primary px-4 text-sm font-medium text-primary-fg hover:opacity-90"
>
Delete
</button>
<div class="w-[min(92vw,24rem)] rounded-2xl border border-border bg-background p-6 shadow-2xl shadow-black/30 ring-1 ring-border/60">
<h2 class="text-lg font-semibold tracking-tight text-fg">Delete project?</h2>
<p class="text-sm text-muted-fg">This cannot be undone.</p>
<div class="mt-4 flex justify-end gap-2">
<button data-close type="button" class="inline-flex h-9 items-center rounded-full bg-secondary px-4 text-sm font-medium text-fg ring-1 ring-border hover:bg-accent">Cancel</button>
<button data-close type="button" class="inline-flex h-9 items-center rounded-full bg-primary px-4 text-sm font-medium text-primary-fg hover:opacity-90">Delete</button>
</div>
</div>
</dom-dialog>
<script type="module">
import '@getdom/studio/headless/dialog.js';
</script>
Demo
Without a trigger slot
Give the host an id, then open from any button with command="open" commandfor="…", or call open() / close() / toggle() in script.
programmatic.htmlhtml
<div class="flex flex-wrap items-center justify-center gap-2">
<button
type="button"
command="open"
commandfor="demo-dialog"
class="inline-flex h-10 items-center rounded-full bg-primary px-4 text-sm font-medium text-primary-fg hover:opacity-90"
>
Open (commandfor)
</button>
<button
type="button"
id="dialog-open"
class="inline-flex h-10 items-center rounded-full bg-secondary px-4 text-sm font-medium text-fg ring-1 ring-border hover:bg-accent"
>
Open (script)
</button>
<button
type="button"
id="dialog-close"
class="inline-flex h-10 items-center rounded-full px-4 text-sm font-medium text-muted-fg hover:bg-secondary hover:text-fg"
>
Close
</button>
</div>
<dom-dialog id="demo-dialog">
<div class="w-[min(92vw,24rem)] rounded-2xl border border-border bg-background p-6 shadow-2xl shadow-black/30 ring-1 ring-border/60">
<h2 class="text-lg font-semibold tracking-tight text-fg">No trigger slot</h2>
<p class="mt-2 text-sm text-muted-fg">
Open from a button elsewhere via <code class="text-fg">commandfor</code> or
<code class="text-fg">.open()</code> on the host.
</p>
<div class="mt-6 flex justify-end">
<button
data-close
type="button"
class="inline-flex h-9 items-center rounded-full bg-primary px-4 text-sm font-medium text-primary-fg hover:opacity-90"
>
Done
</button>
</div>
</div>
</dom-dialog>
<script type="module">
import '@getdom/studio/headless/dialog.js';
const dialog = document.getElementById('demo-dialog');
const openBtn = document.getElementById('dialog-open');
const closeBtn = document.getElementById('dialog-close');
openBtn?.addEventListener('click', () => dialog.open());
closeBtn?.addEventListener('click', () => dialog.close());
</script>
Usage
Plain HTML
<dom-dialog id="confirm-dialog">
<h2>Delete project?</h2>
<p>This cannot be undone.</p>
<button data-close>Cancel</button>
</dom-dialog>
<button type="button" command="open" commandfor="confirm-dialog">Delete</button>
<script type="module">
import '@getdom/studio/headless/dialog.js';
const dlg = document.getElementById('confirm-dialog');
dlg.addEventListener('dom:close', () => console.log('closed'));
</script> Register every <dom-*> in one import: import '@getdom/studio/headless'.
Slots
| Name | Scope | Description |
|---|---|---|
| #trigger | — | Optional. Element that opens the dialog when clicked. Omit when using commandfor or programmatic control. |
| #(default) | — | Dialog body. Anything that is not the trigger goes here. |
Attributes
| Name | Type | Description |
|---|---|---|
| id | string | Host id — used by commandfor on external buttons (e.g. commandfor="my-dialog"). |
| open | boolean | Reflects open state. Setting it shows the dialog (showModal), removing it closes it. |
| static | boolean | Modal cannot be dismissed by backdrop click or Esc — only data-close, .close(), or removing open. |
| no-backdrop | boolean | Hides the visual backdrop while keeping native modal top-layer behaviour. |
From dom-dialog.__doc.attributes.
Events
| Name | Payload | Description |
|---|---|---|
| dom:open | — | Fired when the dialog opens. |
| dom:close | — | Fired when the dialog closes (Esc, .close(), backdrop, or data-close). |
Names auto-detected from defineEmits and source emit() calls; payload and description from __doc.events when present.
Keyboard
- EscCloses the topmost open dialog unless `static` is set.
- Tab / Shift+TabFocus stays within the dialog while open (native top-layer).
- Click backdropDismisses the dialog (disable with `static`).
Related