Component
<dom-drawer>
custom elementLight-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.
<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.
<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.
<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.
<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
| Name | Scope | Description |
|---|---|---|
| #trigger | — | Element 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
| Name | Type | Description |
|---|---|---|
| open | boolean | Reflects open state. Set to show the drawer; remove or set false to hide. |
| show | boolean | Alias for `open` (Headless UI naming). |
| side | string | Edge the panel slides from: `left`, `right`, or `bottom` (default `right`). |
| static | boolean | Disables backdrop-click dismiss. |
| enter | string | Tailwind classes for the enter transition (active state). |
| enter-from | string | Tailwind classes applied before the enter transition runs. |
| enter-to | string | Tailwind classes applied for the enter transition end state. |
| leave | string | Tailwind classes for the leave transition (active state). |
| leave-from | string | Tailwind classes applied before the leave transition runs. |
| leave-to | string | Tailwind classes applied for the leave transition end state. |
From dom-drawer.__doc.attributes.
Events
| Name | Payload | Description |
|---|---|---|
| dom:open | — | Fired when the drawer opens. |
| dom:close | — | Fired 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