Drawer
Drawer — a panel anchored to an edge of the viewport.
Examples
<button class="btn btn-outline" type="button"
command="show-modal" commandfor="demo-drawer-end">Filters</button>
<button class="btn btn-outline" type="button"
command="show-modal" commandfor="demo-drawer-start">Menu</button>
<dialog class="drawer drawer-end drawer-divided" id="demo-drawer-end"
data-ui="drawer" aria-labelledby="demo-drawer-end-title">
<header class="drawer-header">
<h2 class="drawer-title" id="demo-drawer-end-title">Filters</h2>
<button class="btn btn-quiet btn-icon drawer-close" type="button"
command="close" commandfor="demo-drawer-end" aria-label="Close">
<svg class="icon" aria-hidden="true"><use href="#icon-x"></use></svg>
</button>
</header>
<div class="drawer-body">
<div class="field field-inline">
<input class="check" id="demo-drawer-instock" type="checkbox" checked />
<label class="field-label" for="demo-drawer-instock">In stock only</label>
</div>
<div class="field">
<label class="field-label" for="demo-drawer-sort">Sort by</label>
<select class="select" id="demo-drawer-sort">
<option>Relevance</option>
<option>Price</option>
</select>
</div>
</div>
<footer class="drawer-footer">
<button class="btn btn-primary" type="button"
command="close" commandfor="demo-drawer-end">Apply</button>
</footer>
</dialog>
<dialog class="drawer drawer-start" id="demo-drawer-start"
data-ui="drawer" aria-labelledby="demo-drawer-start-title">
<header class="drawer-header">
<h2 class="drawer-title" id="demo-drawer-start-title">Menu</h2>
<button class="btn btn-quiet btn-icon drawer-close" type="button"
command="close" commandfor="demo-drawer-start" aria-label="Close">
<svg class="icon" aria-hidden="true"><use href="#icon-x"></use></svg>
</button>
</header>
<nav class="drawer-body" aria-label="Example">
<ul class="menu-list">
<li><a class="link" href="#demo-drawer-start">Overview</a></li>
<li><a class="link" href="#demo-drawer-start">Reports</a></li>
<li><a class="link" href="#demo-drawer-start">Settings</a></li>
</ul>
</nav>
</dialog>A drawer is a <dialog>. The top layer, the backdrop, inert on the rest of the page, the focus trap, Escape and focus returned to the invoker are all the browser’s, exactly as for a centred modal.
What the platform does not give a dialog is closing when you click outside it, so data-ui="drawer" adds that — press and release are both checked, so a selection dragged out of a field inside the panel does not dismiss it, and data-drawer-dismiss="none" turns it off for a panel holding a form. The placements are logical, so .drawer-end is on the left in Arabic with no second rule. A drawer holding navigation says nothing about its contents — the links are a <nav> with a <ul>, because that is what they are.
<button class="btn btn-outline" type="button"
command="show-modal" commandfor="demo-drawer-sheet">Share</button>
<dialog class="drawer drawer-block-end drawer-sm" id="demo-drawer-sheet"
data-ui="drawer" aria-labelledby="demo-drawer-sheet-title">
<span class="drawer-handle" aria-hidden="true"></span>
<header class="drawer-header">
<h2 class="drawer-title" id="demo-drawer-sheet-title">Share this page</h2>
</header>
<div class="drawer-body">
<p>Anyone with the link can read it.</p>
</div>
<footer class="drawer-footer">
<button class="btn btn-quiet" type="button"
command="close" commandfor="demo-drawer-sheet">Cancel</button>
<button class="btn btn-primary" type="button"
command="close" commandfor="demo-drawer-sheet">Copy link</button>
</footer>
</dialog>A bottom sheet sizes to its content up to --drawer-size, which is what makes a short sheet short.
Drag the grabber down and it closes. .drawer-handle is both the affordance and the opt-in — the gesture is wired only where the grabber is, so a sheet that can be dragged looks draggable and one that cannot does not. It closes past a third of its own height, or on a fast flick whatever the distance, and a drag the wrong way is damped rather than blocked. Escape and the close button remain the dismissals that work without a pointer; the gesture is an addition, never the only route.
Tokens 22
Level 2, declared on .drawer itself. Set any of them on that selector to restyle this component without touching the skin.
| Token | Default |
|---|---|
| --drawer-backdrop | var(--color-overlay) |
| --drawer-backdrop-filter | none |
| --drawer-divider-color | var(--color-border-subtle) |
| --drawer-duration | var(--duration-normal) |
| --drawer-enter | 0 0 |
| --drawer-footer-gap | var(--space-2) |
| --drawer-handle-block-size | 0.25rem |
| --drawer-handle-color | var(--color-border-strong) |
| --drawer-handle-inline-size | 2.5rem |
| --drawer-handle-padding-block | var(--space-3) |
| --drawer-size | 22rem |
| --drawer-title-font-family | var(--font-primary) |
| --drawer-title-font-size | var(--font-size-lg) |
| --drawer-title-font-weight | var(--font-weight-semibold) |
| --drawer-bg | var(--color-surface-raised) |
| --drawer-border-color | var(--color-border-subtle) |
| --drawer-border-width | var(--border-width) |
| --drawer-color | var(--color-on-surface) |
| --drawer-gap | var(--space-4) |
| --drawer-padding | var(--space-5) |
| --drawer-radius | var(--radius-box) |
| --drawer-shadow | var(--elevation-overlay) |
Variants and states
Variants
.drawer-end.drawer-lg.drawer-sm
Inside it
.drawer-header.drawer-body.drawer-handle.drawer-start.drawer-block-end.drawer-block-start.drawer-divided.drawer-footer.drawer-close.drawer-title
State it reads
Read from the platform, never mirrored into a class that could disagree with it.
[open]data-draggingdata-legacydata-ui
Before you ship it
This one needs a script. Opt the markup in with data-ui="drawer" and import import 'mostlycss/js/components/drawer' — that specifier and not the package root, which is a bundle carrying a second copy of the registry, so a page holding both initialises every element twice. What you lose without the script is listed below — for some components that is only polish, for others it is the whole interaction.
What you have to do 1 requirements
- Name it —
aria-labelledbyon the<dialog>pointing at.drawer-title, or anaria-label. Name the icon-only close button. If the drawer holds the site navigation, the links inside it are a<nav>with a<ul>; a drawer is a container and says nothing about what is in it.
src/css/components/drawer.css · npx mostlycss add drawer