Drawer Dialog
Slide-in panel using native <dialog> element. Side drawers for navigation, settings, or detail views.
Overview
A drawer is a dialog that slides in from the side of the screen. It uses the native <dialog> element for built-in accessibility (focus trapping, escape key, backdrop). CSS handles the slide animation with @starting-style for entry transitions.
Key features:
- Native
<dialog>with focus trapping and escape key - Slides from left (
start) or right (end) - CSS animation with
@starting-style - Full-height panel with scrollable body
Basic Drawer
A right-side drawer with header, scrollable body, and footer actions.
Basic drawer
<button type="button" id="open-drawer">Open Drawer</button> <dialog id="drawer" class="drawer" data-position="end"> <layout-stack data-layout-gap="l"> <header> <h2>Drawer Title</h2> <button type="button" class="ghost" id="close-drawer" aria-label="Close"> ✕ </button> </header> <div class="drawer-body"> <p>Drawer content slides in from the side.</p> </div> <footer> <layout-cluster data-layout-justify="flex-end" data-layout-gap="m"> <button type="button" class="secondary" id="cancel-drawer">Cancel</button> <button type="button" class="primary">Save</button> </layout-cluster> </footer> </layout-stack></dialog> <script> const drawer = document.getElementById('drawer'); document.getElementById('open-drawer').onclick = () => drawer.showModal(); document.getElementById('close-drawer').onclick = () => drawer.close(); document.getElementById('cancel-drawer').onclick = () => drawer.close();</script>
Drawer Styles
Add these styles to your project. The data-position attribute controls which side the drawer slides from.
Drawer styles
.drawer { padding: 0; border: none; max-width: min(90vw, 400px); max-height: 100vh; height: 100vh; margin: 0; border-radius: 0; box-shadow: var(--shadow-xl);} /* Slide from right (end) */.drawer[data-position="end"] { margin-inline-start: auto;} /* Slide from left (start) */.drawer[data-position="start"] { margin-inline-end: auto;} .drawer::backdrop { background: var(--color-overlay-strong); backdrop-filter: blur(4px);} .drawer > layout-stack { padding: var(--size-l); height: 100%;} .drawer header { display: flex; justify-content: space-between; align-items: center;} .drawer header h2 { margin: 0; font-size: var(--font-size-xl);} .drawer-body { flex: 1; overflow-y: auto;} /* Animation */.drawer { transform: translateX(100%); transition: transform var(--duration-normal, 300ms) var(--ease-out, ease-out), display var(--duration-normal, 300ms) allow-discrete, overlay var(--duration-normal, 300ms) allow-discrete;} .drawer[data-position="start"] { transform: translateX(-100%);} .drawer[open] { transform: translateX(0);} @starting-style { .drawer[open] { transform: translateX(100%); } .drawer[open][data-position="start"] { transform: translateX(-100%); }} /* Reduced motion */@media (prefers-reduced-motion: reduce) { .drawer { transition-duration: 0s; }}
Navigation Drawer
A left-side drawer for mobile navigation. Use data-position="start" for conventional left-side navigation.
Navigation drawer
<dialog id="nav-drawer" class="drawer" data-position="start"> <layout-stack data-layout-gap="l"> <header> <h2>Navigation</h2> <button type="button" class="ghost" aria-label="Close">✕</button> </header> <nav> <ul> <li><a href="/">Home</a></li> <li><a href="/about/">About</a></li> <li><a href="/products/">Products</a></li> <li><a href="/contact/">Contact</a></li> </ul> </nav> </layout-stack></dialog>
Usage Notes
- Position: Use
data-position="end"for settings/details,data-position="start"for navigation - Width: Adjust
max-widthin the CSS (default:min(90vw, 400px)) - Scrolling: The
.drawer-bodyscrolls independently withoverflow-y: auto - Click outside: Add a click listener on the dialog for backdrop dismissal
- Animation: Uses
@starting-stylefor entry animation. Respectsprefers-reduced-motion
Related
Dialog Element
Native dialog documentation
Modal Dialog
Centered modal patterns
Bottom Sheet
Mobile action menus and share sheets