Drawer
A slide-in panel that overlays page content from any edge of the screen. Built on the native <dialog> element for correct top-layer stacking, focus trapping, and Escape-to-close behavior.
Placements
Use open as the controlled state and update it from open-change; use default-open only to initialize an uncontrolled drawer. Use placement to control which edge the drawer slides in from: left, right (default), top, or bottom.
<ore-button id="open-left">Left</ore-button>
<ore-button id="open-right">Right</ore-button>
<ore-button id="open-top">Top</ore-button>
<ore-button id="open-bottom">Bottom</ore-button>
<ore-drawer id="drawer-left" placement="left" label="Left drawer" dismissible>
<p>Slides in from the left edge.</p>
</ore-drawer>
<ore-drawer id="drawer-right" placement="right" label="Right drawer" dismissible>
<p>Slides in from the right edge (default).</p>
</ore-drawer>
<ore-drawer id="drawer-top" placement="top" label="Top drawer" dismissible>
<p>Slides down from the top edge.</p>
</ore-drawer>
<ore-drawer id="drawer-bottom" placement="bottom" label="Bottom drawer" dismissible>
<p>Slides up from the bottom edge.</p>
</ore-drawer>
<script>
['left', 'right', 'top', 'bottom'].forEach(function (p) {
document.getElementById('open-' + p).addEventListener('click', function () {
document.getElementById('drawer-' + p).setAttribute('open', '');
});
});
</script>Sizes
<ore-button id="open-sm">sm</ore-button>
<ore-button id="open-md">md</ore-button>
<ore-button id="open-lg">lg</ore-button>
<ore-button id="open-full">full</ore-button>
<ore-drawer id="drawer-sm" size="sm" label="Small drawer" dismissible>
<p>Compact drawer — ideal for quick-action panels.</p>
</ore-drawer>
<ore-drawer id="drawer-md" size="md" label="Medium drawer" dismissible>
<p>The default size. Suitable for most use cases.</p>
</ore-drawer>
<ore-drawer id="drawer-lg" size="lg" label="Large drawer" dismissible>
<p>More room for forms or detailed content.</p>
</ore-drawer>
<ore-drawer id="drawer-full" size="full" label="Full drawer" dismissible>
<p>Takes up the full width or height of the viewport.</p>
</ore-drawer>
<script>
['sm', 'md', 'lg', 'full'].forEach(function (s) {
document.getElementById('open-' + s).addEventListener('click', function () {
document.getElementById('drawer-' + s).setAttribute('open', '');
});
});
</script>With Header and Footer Slots
Use the header slot to replace the default title bar and the footer slot for action buttons.
<ore-button id="open-slots-btn">Open with slots</ore-button>
<ore-drawer id="drawer-slots" label="Edit profile" dismissible>
<div slot="header">
<strong>Edit profile</strong>
<ore-badge color="primary" size="sm">Beta</ore-badge>
</div>
<div style="display:flex; flex-direction:column; gap:1rem;">
<ore-input label="Display name" placeholder="Your name"></ore-input>
<ore-input label="Email" placeholder="you@example.com"></ore-input>
</div>
<div slot="footer">
<ore-button variant="ghost" id="cancel-slots-btn">Cancel</ore-button>
<ore-button color="primary">Save</ore-button>
</div>
</ore-drawer>
<script>
document.getElementById('open-slots-btn').addEventListener('click', function () {
document.getElementById('drawer-slots').setAttribute('open', '');
});
document.getElementById('cancel-slots-btn').addEventListener('click', function () {
document.getElementById('drawer-slots').removeAttribute('open');
});
</script>Drag Handle Placement
Use drag-handle-placement to control whether the swipe handle sits outside the panel edge or inset inside it.
<ore-button id="open-handle-outside">Outside handle</ore-button>
<ore-button id="open-handle-inset">Inset handle</ore-button>
<ore-drawer
id="drawer-handle-outside"
label="Outside drag handle"
placement="right"
drag-handle-placement="outside"
dismissible>
<p>Default behavior. The drag handle sits outside the drawer edge.</p>
</ore-drawer>
<ore-drawer
id="drawer-handle-inset"
label="Inset drag handle"
placement="right"
drag-handle-placement="inset"
dismissible>
<p>Inset behavior. The drag handle stays inside the drawer edge.</p>
</ore-drawer>
<script>
document.getElementById('open-handle-outside').addEventListener('click', function () {
document.getElementById('drawer-handle-outside').setAttribute('open', '');
});
document.getElementById('open-handle-inset').addEventListener('click', function () {
document.getElementById('drawer-handle-inset').setAttribute('open', '');
});
</script>Backdrop Styles (backdrop)
Use backdrop to match dialog behavior:
opaque(default): dim overlayblur: dim + blurtransparent: no overlay
<ore-button id="open-backdrop-opaque">Opaque</ore-button>
<ore-button id="open-backdrop-blur">Blur</ore-button>
<ore-button id="open-backdrop-transparent">Transparent</ore-button>
<ore-drawer id="drawer-backdrop-opaque" label="Opaque backdrop" placement="right" backdrop="opaque" dismissible>
<p>Default dimmed backdrop.</p>
</ore-drawer>
<ore-drawer id="drawer-backdrop-blur" label="Blur backdrop" placement="right" backdrop="blur" dismissible>
<p>Backdrop uses blur + dim overlay.</p>
</ore-drawer>
<ore-drawer
id="drawer-backdrop-transparent"
label="Transparent backdrop"
placement="right"
backdrop="transparent"
dismissible>
<p>No dimmed backdrop, drawer still behaves like a dialog.</p>
</ore-drawer>
<script>
['opaque', 'blur', 'transparent'].forEach((kind) => {
document.getElementById('open-backdrop-' + kind).addEventListener('click', () => {
document.getElementById('drawer-backdrop-' + kind).setAttribute('open', '');
});
});
</script>Listening to Events
<ore-drawer id="my-drawer" label="Notifications" dismissible>
<p>You have no new notifications.</p>
</ore-drawer>
<script type="module">
import '@vielzeug/refine/drawer';
const drawer = document.getElementById('my-drawer');
drawer.addEventListener('open-change', (e) => console.log('drawer open:', e.detail.open, e.detail.reason));
drawer.addEventListener('close-request', (e) => {
if (e.detail.reason === 'outside-click') {
console.log('close requested from backdrop click');
}
});
</script>API Reference
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Controlled visibility |
default-open | boolean | false | Initial visibility when open is not provided |
placement | 'left' | 'right' | 'top' | 'bottom' | 'right' | Edge the drawer slides in from |
size | 'sm' | 'md' | 'lg' | 'full' | 'md' | Panel width (or height for top/bottom placements) |
label | string | — | Accessible title shown in the header bar |
drag-handle-placement | 'outside' | 'inset' | 'outside' | Position of the swipe drag handle |
dismissible | boolean | true | Shows a close (×) button in the header |
backdrop | 'opaque' | 'blur' | 'transparent' | 'opaque' | Backdrop style, matching ore-dialog |
persistent | boolean | false | Prevents backdrop click from requesting close |
Slots
| Slot | Description |
|---|---|
| (default) | Drawer body content |
header | Replace the default header bar (overrides the label title) |
footer | Footer area, typically used for action buttons |
Events
| Event | Detail | Description |
|---|---|---|
open-change | { open: boolean, reason: string } | Fired whenever the drawer opens or closes |
close-request | { placement: 'left' | 'right' | 'top' | 'bottom', reason: 'trigger' | 'escape' | 'outside-click' } | Fired before close and can be prevented |
CSS Custom Properties
| Property | Description |
|---|---|
--drawer-backdrop-bg | Backdrop background color |
--drawer-bg | Panel background color |
--drawer-size | Panel width (horizontal) or height (vertical) |
--drawer-shadow | Panel box shadow |
--drawer-panel-blur | Panel backdrop blur amount |
Accessibility
The drawer component follows the WAI-ARIA Dialog Pattern best practices.
The panel uses role="dialog" with aria-modal="true" to signal that content outside is inert. Always provide a label attribute to give screen readers a descriptive panel title. When dismissible is set, the close button carries aria-label="Close" automatically.
Tab and Shift+Tab move focus between focusable elements inside the panel. Escape closes the drawer when dismissible is set. Focus moves into the panel on open and returns to the trigger element on close.
Related Components
- Dialog — modal dialog for confirmations and focused tasks