Component

<dom-dialog>

custom element

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

NameScopeDescription
#triggerOptional. 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

NameTypeDescription
idstringHost id — used by commandfor on external buttons (e.g. commandfor="my-dialog").
openbooleanReflects open state. Setting it shows the dialog (showModal), removing it closes it.
staticbooleanModal cannot be dismissed by backdrop click or Esc — only data-close, .close(), or removing open.
no-backdropbooleanHides the visual backdrop while keeping native modal top-layer behaviour.

From dom-dialog.__doc.attributes.

Events

NamePayloadDescription
dom:openFired when the dialog opens.
dom:closeFired 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

See also

← Headless overview