Component

<dom-drawer>

custom element

Light-DOM drawer primitive anchored to the left, right, or bottom edge. It owns open state, ARIA, focus trap, scroll lock, Esc/backdrop dismiss, and events while keeping panel/backdrop styling in editable Tailwind classes.

Demo

Right edge (default)

Trigger via slot="trigger" — the element wires the click handler and ARIA for you.

navigation.htmlhtml
<dom-drawer side="right" class="bg-background">
	<button
		slot="trigger"
		type="button"
		class="inline-flex h-10 items-center rounded-full bg-secondary px-4 text-sm font-medium text-fg ring-1 ring-border transition hover:bg-accent"
	>
		Open menu
	</button>
	<nav class="flex flex-col h-full skin-raised text-fg">
		<a href="#" data-close class="border-b border-border px-4 py-4 text-sm font-medium hover:bg-secondary/60">Home</a>
		<a href="#" data-close class="border-b border-border px-4 py-4 text-sm font-medium text-muted-fg hover:bg-secondary/60 hover:text-fg">Projects</a>
		<a href="#" data-close class="border-b border-border px-4 py-4 text-sm font-medium text-muted-fg hover:bg-secondary/60 hover:text-fg">Settings</a>
	</nav>
</dom-drawer>

<script type="module">
	import '@getdom/studio/headless/drawer.js';
</script>

Demo

Left edge

Set the side attribute to left for filter panels and secondary surfaces.

filters.htmlhtml
<dom-drawer side="left">
	<button
		slot="trigger"
		type="button"
		class="inline-flex h-10 items-center rounded-full bg-secondary px-4 text-sm font-medium text-fg ring-1 ring-border transition hover:bg-accent"
	>
		Open filters
	</button>
	<div class="space-y-4 skin-raised h-full p-4 text-sm text-muted-fg">
		<p>Sort by date, status, or assignee.</p>
		<button
			type="button"
			data-close
			class="inline-flex h-10 w-full items-center justify-center rounded-full bg-secondary px-4 text-sm font-medium text-fg ring-1 ring-border transition hover:bg-accent"
		>
			Apply
		</button>
	</div>
</dom-drawer>

<script type="module">
	import '@getdom/studio/headless/drawer.js';
</script>

Demo

Custom transitions

Headless UI–style enter/leave attributes with Tailwind classes on the panel instead of the default slide animation.

transitions.htmlhtml
<dom-drawer
	side="right"
	enter="transition duration-1000 ease-out"
	enter-from="opacity-0 scale-50"
	enter-to="opacity-100 scale-100"
	leave="transition duration-1000 ease-in"
	leave-from="opacity-100 scale-100"
	leave-to="opacity-0 scale-50"
>
	<button
		slot="trigger"
		type="button"
		class="inline-flex h-10 items-center rounded-full bg-secondary px-4 text-sm font-medium text-fg ring-1 ring-border transition hover:bg-accent"
	>
		Open (fade)
	</button>
	<div class="flex h-full skin-raised flex-col gap-4 p-6">
		<h2 class="text-base font-semibold text-fg">Custom transitions</h2>
		<p class="text-sm text-muted-fg">
			Set <code>enter</code>, <code>enter-from</code>, <code>enter-to</code>, and matching
			<code>leave-*</code> attributes with Tailwind classes
		</p>
		<button
			data-close
			type="button"
			class="inline-flex h-10 w-fit items-center rounded-full bg-primary px-4 text-sm font-medium text-primary-fg transition hover:opacity-90"
		>
			Close
		</button>
	</div>
</dom-drawer>

<script type="module">
	import '@getdom/studio/headless/drawer.js';
</script>

Demo

Programmatic control

show(), hide(), toggle(), and the open property work on the custom element instance.

programmatic.htmlhtml
<div class="flex flex-wrap items-center justify-center gap-2">
	<button
		type="button"
		id="drawer-open"
		class="inline-flex h-10 items-center rounded-full bg-primary px-4 text-sm font-medium text-primary-fg transition hover:opacity-90"
	>
		Open
	</button>
	<button
		type="button"
		id="drawer-close"
		class="inline-flex h-10 items-center rounded-full bg-secondary px-4 text-sm font-medium text-fg ring-1 ring-border transition hover:bg-accent"
	>
		Close
	</button>
	<button
		type="button"
		id="drawer-toggle"
		class="inline-flex h-10 items-center rounded-full px-4 text-sm font-medium text-muted-fg transition hover:bg-secondary hover:text-fg"
	>
		Toggle
	</button>
</div>

<dom-drawer id="demo-drawer" side="right">
	<div class="skin-raised h-full p-4 text-sm text-muted-fg">
		<p class="font-semibold text-fg">Via JavaScript</p>
		<p class="mt-2">Call <code>show()</code>, <code>hide()</code>, or <code>toggle()</code> on the element — or set the <code>open</code> attribute.</p>
		<button type="button" data-close class="mt-4 text-sm font-medium text-fg hover:underline">Dismiss</button>
	</div>
</dom-drawer>

<script type="module">
	import '@getdom/studio/headless/drawer.js';

	// `root` is injected by the doc site preview (runHtmlExample.js).
	// Standalone: const root = document.getElementById('your-wrapper');
	const drawer = document.getElementById('demo-drawer');
	document.getElementById('drawer-open').addEventListener('click', () => drawer.show());
	document.getElementById('drawer-close').addEventListener('click', () => drawer.hide());
	document.getElementById('drawer-toggle').addEventListener('click', () => drawer.toggle());
</script>

Usage

Plain HTML

<dom-drawer side="right">
  <button slot="trigger">Menu</button>
  <nav><!-- panel content --></nav>
  <button data-close>Close</button>
</dom-drawer>

<script type="module">
  import '@getdom/studio/headless/drawer.js';
</script>

Register every <dom-*> in one import: import '@getdom/studio/headless'.

Slots

NameScopeDescription
#triggerElement that opens the drawer when clicked. You can also use data-trigger.
#(default)Panel body. If no data-panel is provided, the drawer wraps this content in light-DOM overlay/backdrop/panel parts.

Attributes

NameTypeDescription
openbooleanReflects open state. Set to show the drawer; remove or set false to hide.
showbooleanAlias for `open` (Headless UI naming).
sidestringEdge the panel slides from: `left`, `right`, or `bottom` (default `right`).
staticbooleanDisables backdrop-click dismiss.
enterstringTailwind classes for the enter transition (active state).
enter-fromstringTailwind classes applied before the enter transition runs.
enter-tostringTailwind classes applied for the enter transition end state.
leavestringTailwind classes for the leave transition (active state).
leave-fromstringTailwind classes applied before the leave transition runs.
leave-tostringTailwind classes applied for the leave transition end state.

From dom-drawer.__doc.attributes.

Events

NamePayloadDescription
dom:openFired when the drawer opens.
dom:closeFired when the drawer closes (Esc, backdrop, data-close, or programmatic).

Names auto-detected from defineEmits and source emit() calls; payload and description from __doc.events when present.

Keyboard

  • EscCloses the drawer.
  • Tab / Shift+TabFocus cycles within the panel while open.
  • Click backdropDismisses the drawer (disable with `static`).
  • Drag handleDrag `[data-drag-handle]` toward the closing edge to dismiss the drawer.

Related

See also

← Headless overview