HyDrawer
A drawer component that slides in from the edge of the screen.
Key Features
- Slides in from any edge (start, end, top, bottom)
- Uses logical properties for RTL support
- Three variants: temporary (modal), persistent, permanent
- Mini/collapsed mode with icon-only display
- Configurable size presets or custom values
- Focus trapping and management (temporary variant)
- Cancelable close events
Variants
- Temporary (default): Overlay drawer, modal by default (scrim + focus trap); set
modal={false}for a non-modal floating panel (Popover API, no scrim, page stays interactive) - Persistent: Toggleable drawer without overlay, content shifts aside
- Permanent: Always visible, integrated into layout
- Responsive: One declaration, one content source — morphs between a docked inline rail (region ≥
docked-at) and an overlay sheet with a self-trigger (region <docked-at). A ResizeObserver on the parent region drives the switch and reflectsdata-mode="docked|overlay"so the surrounding layout can key on the active mode at the same breakpoint.
Mini Mode
When mini is enabled, the drawer collapses to show only icons. Use expand-on-hover to expand the mini drawer when hovered.
Native Dialog Approach
The temporary variant leverages the browser's native <dialog> element which provides:
- Top Layer Rendering: Drawer appears above all other content automatically
- Inert Background: Modal variant makes outside content non-interactive
- Backdrop Support: Native
::backdroppseudo-element for overlay styling - Escape Key Handling: Built-in keyboard dismissal with the
cancelevent - Accessibility: Implicit
role="dialog"andaria-modal="true"
Initial Focus
On show, the drawer follows the same [autofocus] discovery contract as hy-dialog: it queries for a slotted descendant marked with autofocus, emits a cancelable initial-focus event, and (if not prevented) focuses the resolved target. When no marker is set, focus falls back to the close button (or collapse trigger) so keyboard users land somewhere sensible. See .claude/rules/focus.md for the contract.
Accessibility Features
- Native dialog accessibility (role="dialog", aria-modal based on variant)
- Configurable aria-labelledby and aria-label
- Configurable close button label for internationalization (close-label)
- Configurable collapse trigger label for internationalization (collapse-label)
- Focus restoration to trigger element on close
- Autofocus discovery via
[autofocus]marker; cancelable viainitial-focusevent - Escape key dismissal with cancelable event
- Backdrop click dismissal
- Body scroll lock when open (temporary variant)
- Focus management (focus not obscured)
- Screen reader announcements on open/close
Examples
Basic Usage (Temporary/Modal)
<hy-drawer label="Settings" open>
<p>Configure your preferences here.</p>
</hy-drawer>Self-managing trigger (no open wiring)
<hy-drawer label="Filters" placement="end">
<hy-button slot="trigger">Filters</hy-button>
<p>Filter facets go here.</p>
</hy-drawer>Responsive morph (docked rail ↔ overlay sheet)
<!-- Wide region: docked inline rail. Narrow region: a "Filters" trigger
opens an overlay sheet. Same fragment, same single content source. -->
<hy-drawer variant="responsive" docked-at="md" placement="start" label="Filters">
<hy-button slot="trigger">Filters</hy-button>
<p>Filter facets — authored once.</p>
</hy-drawer>Persistent Drawer (Sidebar)
<hy-drawer variant="persistent" placement="start" open>
<nav>
<a href="/home">Home</a>
<a href="/settings">Settings</a>
</nav>
</hy-drawer>Permanent Drawer (Always Visible)
<hy-drawer variant="permanent" placement="start">
<nav>Navigation always visible</nav>
</hy-drawer>Mini Drawer with Expand on Hover
<hy-drawer variant="permanent" placement="start" mini expand-on-hover>
<nav>
<hy-icon name="home"></hy-icon> <span>Home</span> <hy-icon name="settings"></hy-icon>
<span>Settings</span>
</nav>
</hy-drawer>Collapsible Sidebar with Collapse Trigger
<hy-drawer variant="permanent" placement="start" collapse-trigger>
<nav>
<hy-icon name="home"></hy-icon> <span>Home</span> <hy-icon name="settings"></hy-icon>
<span>Settings</span>
</nav>
</hy-drawer>With Placement
<hy-drawer label="Navigation" placement="start" open>
<nav>Navigation items here</nav>
</hy-drawer>With Footer Actions
<hy-drawer label="Edit Profile">
<form>Form content here</form>
<div slot="footer">
<hy-button variant="neutral">Cancel</hy-button>
<hy-button variant="brand">Save</hy-button>
</div>
</hy-drawer>Programmatic Control
<hy-button id="openBtn">Open Drawer</hy-button>
<hy-drawer id="myDrawer" label="Settings">
<p>Drawer content</p>
</hy-drawer>
<script>
const drawer = document.getElementById('myDrawer');
const openBtn = document.getElementById('openBtn');
openBtn.addEventListener('click', () => drawer.show());
// Listen for close requests (cancelable)
drawer.addEventListener('request-close', (e) => {
if (hasUnsavedChanges) {
e.preventDefault(); // Keep drawer open
showUnsavedWarning();
}
});
</script>Custom Size
<hy-drawer label="Wide Drawer" size="600px" placement="end">
<p>This drawer has a custom width.</p>
</hy-drawer>Events
- show - Fired when the drawer opens. Detail:
{ source: HyVisibilitySource } - hide - Fired when the drawer closes (after animation). Detail:
{ source: HyVisibilitySource } - request-close - Fired before closing; cancelable to prevent close. Detail:
{ source: HyVisibilitySource } - initial-focus - Fired on
showafter the autofocus discovery runs. Cancelable: callevent.preventDefault()to skip the default focus and apply your own. Detail:HyInitialFocusDetail
Slots
- trigger - An element that toggles the drawer on click (self-managing, no
openwiring required). ARIA (aria-haspopup,aria-controls,aria-expanded) is applied automatically and focus returns to the trigger on close. A controlledopenstill wins when both are present. Most useful on thetemporaryvariant. - default - The main content displayed in the drawer body
- header - Custom header content that replaces the default title.
- header-actions - Additional actions in the header (alongside close button)
- footer - Footer content, typically action buttons
Methods
willUpdate()
Generates unique IDs before the first render to prevent update-after-update warnings.
firstUpdated()
Resolve a for= trigger and apply initial trigger ARIA after the first render (the <dialog> id exists by now).
updated()
Single source of truth: all side effects for open/close happen here.
disconnectedCallback()
Clean up when element is removed from the DOM.
show()
Opens the drawer programmatically. Has no effect on permanent variant (always visible).
hide()
Closes the drawer programmatically. Emits a cancelable request-close event first. Has no effect on permanent variant (cannot be closed).
Parameters:
source- How the close was triggered