Drawer
Slide-in side panel built on top of native <dialog> semantics. Used for mobile navigation in this docs site, but works as a general-purpose drawer for filters, settings, or off-canvas menus.
The drawer is CSS-driven with a small script that handles open/close, focus management, body scroll lock, and the Escape key. No Vue or framework runtime required — just the Drawer.astro component (or copy the HTML recipe).
Live demo
Default (Medium, Left-aligned)
---
import Drawer from 'spoko-design-system/components/Drawer.astro';
---<button type="button" class="btn-primary btn-normal" aria-controls="demo-drawer" aria-expanded="false">
Open drawer
</button><Drawer id="demo-drawer" label="Settings & Navigation">
<div class="p-6 flex flex-col gap-6">
<h3 class="text-xl font-textbold mb-4">Navigation</h3>
<ul class="flex flex-col gap-2">
<li><a href="#" data-drawer-close>Dashboard</a></li>
<li><a href="#" data-drawer-close>Products</a></li>
<li><a href="#" data-drawer-close>Settings</a></li>
</ul>
<button type="button" class="btn-secondary btn-sm" data-drawer-close>Close</button>
</div>
</Drawer>Small Width
<button type="button" class="btn-primary btn-normal" aria-controls="small-drawer" aria-expanded="false">
Open small drawer
</button><Drawer id="small-drawer" label="Quick actions">
<div class="p-4 flex flex-col gap-4">
<h3 class="text-lg font-textbold">Quick Actions</h3>
<button class="btn-primary btn-sm w-full">Action 1</button>
<button class="btn-secondary btn-sm w-full" data-drawer-close>Close</button>
</div>
</Drawer>Large Width
<button type="button" class="btn-primary btn-normal" aria-controls="large-drawer" aria-expanded="false">
Open large drawer
</button><Drawer id="large-drawer" label="Detailed panel">
<div class="p-6 flex flex-col gap-6">
<h3 class="text-xl font-textbold">Order Details</h3>
<!-- ... detailed content ... -->
<button type="button" class="btn-secondary w-full" data-drawer-close>Close</button>
</div>
</Drawer>Right-Aligned
<button type="button" class="btn-primary btn-normal" aria-controls="right-drawer" aria-expanded="false">
Open right drawer
</button><Drawer id="right-drawer" label="Side panel" position="right">
<div class="p-6 flex flex-col gap-4">
<h3 class="text-xl font-textbold">Right Panel</h3>
<p>This drawer opens from the right edge.</p>
<button type="button" class="btn-secondary w-full" data-drawer-close>Close</button>
</div>
</Drawer>Variants
Width Variants
Customize drawer width by overriding the .sds-drawer-panel width via CSS:
Position: Right Side
Pass position="right" to slide from the right edge:
For plain HTML, set data-position="right" on the .sds-drawer element — the built-in stylesheet handles the rest.
Wiring triggers
The drawer auto-discovers its trigger via the aria-controls attribute. Any element with aria-controls="<drawer-id>" opens the matching drawer on click. The script also manages aria-expanded for you.
Closing the drawer
Three built-in ways to close — no extra wiring needed:
| Action | Behavior |
|---|---|
| Click backdrop | Closes drawer |
Press Escape |
Closes drawer (when open) |
Click [data-drawer-close] |
Closes drawer (use on any element inside the panel) |
Add data-drawer-close to any close button, link, or icon inside the drawer:
Accessibility
The drawer follows the WAI-ARIA modal dialog pattern:
- Panel uses
role="dialog"+aria-modal="true" aria-labelset from thelabelprop (defaults to"Navigation")- Focus moves to the panel on open, and returns to the trigger on close
- Body scroll is locked while open (
body.drawer-open { overflow: hidden }) Escapekey dismisses the drawer
Always pass a descriptive label when the drawer is not the main navigation — screen readers announce it as the dialog name.
HTML recipe
The drawer is also available as a pure HTML + CSS pattern. Copy this into any framework or plain HTML page that loads the SDS stylesheet:
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8"><</span><span style="color:#85E89D">button</span><span style="color:#B392F0"> type</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"button"</span><span style="color:#B392F0"> aria-controls</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"my-drawer"</span><span style="color:#B392F0"> aria-expanded</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"false"</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> Open</span></span> <span class="line"><span style="color:#E1E4E8"></</span><span style="color:#85E89D">button</span><span style="color:#E1E4E8">></span></span> <span class="line"></span> <span class="line"><span style="color:#E1E4E8"><</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> id</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"my-drawer"</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"sds-drawer"</span><span style="color:#B392F0"> hidden</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"sds-drawer-backdrop"</span><span style="color:#B392F0"> data-drawer-close</span><span style="color:#E1E4E8">></</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">aside</span></span> <span class="line"><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"sds-drawer-panel"</span></span> <span class="line"><span style="color:#B392F0"> role</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"dialog"</span></span> <span class="line"><span style="color:#B392F0"> aria-modal</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"true"</span></span> <span class="line"><span style="color:#B392F0"> aria-label</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"My drawer"</span></span> <span class="line"><span style="color:#B392F0"> tabindex</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"-1"</span></span> <span class="line"><span style="color:#B392F0"> data-drawer-panel</span></span> <span class="line"><span style="color:#E1E4E8"> ></span></span> <span class="line"><span style="color:#6A737D"> <!-- Drawer content here --></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">button</span><span style="color:#B392F0"> type</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"button"</span><span style="color:#B392F0"> data-drawer-close</span><span style="color:#E1E4E8">>Close</</span><span style="color:#85E89D">button</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> </</span><span style="color:#85E89D">aside</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"></</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span></code></pre>You also need the open/close logic. If you’re not using the Astro component, import the helper directly:
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="ts"><code><span class="line"><span style="color:#F97583">import</span><span style="color:#E1E4E8"> { initDrawer } </span><span style="color:#F97583">from</span><span style="color:#9ECBFF"> 'spoko-design-system/src/scripts/drawer'</span><span style="color:#E1E4E8">;</span></span> <span class="line"></span> <span class="line"><span style="color:#B392F0">initDrawer</span><span style="color:#E1E4E8">(</span><span style="color:#9ECBFF">'my-drawer'</span><span style="color:#E1E4E8">);</span></span></code></pre>Props
| Prop | Type | Default | Description |
|---|---|---|---|
id* | string | — | Unique id used by triggers (aria-controls) and the open/close script |
label | string | 'Navigation' | aria-label for the dialog — describes the drawer to screen readers |
position | 'left' | 'right' | 'left' | Edge the drawer slides in from |
Use Cases
Navigation Menu
Typical mobile navigation with links. Close drawer on link click via data-drawer-close:
Filters Panel
Form inputs for filtering product lists or search results:
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8"><</span><span style="color:#FDAEB7;font-style:italic">Drawer</span><span style="color:#B392F0"> id</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"filters"</span><span style="color:#B392F0"> label</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"Product Filters"</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"p-6 flex flex-col gap-4"</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">label</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"block text-sm font-textbold mb-2"</span><span style="color:#E1E4E8">>Price Range</</span><span style="color:#85E89D">label</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">input</span><span style="color:#B392F0"> type</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"range"</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"w-full"</span><span style="color:#B392F0"> min</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"0"</span><span style="color:#B392F0"> max</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"1000"</span><span style="color:#E1E4E8"> /></span></span> <span class="line"><span style="color:#E1E4E8"> </</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> </span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">label</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"block text-sm font-textbold mb-2"</span><span style="color:#E1E4E8">>Category</</span><span style="color:#85E89D">label</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">select</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"w-full input-standard"</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">option</span><span style="color:#E1E4E8">>All</</span><span style="color:#85E89D">option</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">option</span><span style="color:#E1E4E8">>Electronics</</span><span style="color:#85E89D">option</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">option</span><span style="color:#E1E4E8">>Clothing</</span><span style="color:#85E89D">option</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> </</span><span style="color:#85E89D">select</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> </</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span> <span class="line"></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"flex flex-col gap-2"</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">label</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"flex gap-2 cursor-pointer"</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">input</span><span style="color:#B392F0"> type</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"checkbox"</span><span style="color:#E1E4E8"> /> In Stock</span></span> <span class="line"><span style="color:#E1E4E8"> </</span><span style="color:#85E89D">label</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">label</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"flex gap-2 cursor-pointer"</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">input</span><span style="color:#B392F0"> type</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"checkbox"</span><span style="color:#E1E4E8"> /> On Sale</span></span> <span class="line"><span style="color:#E1E4E8"> </</span><span style="color:#85E89D">label</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> </</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span> <span class="line"></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">button</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"btn-primary w-full"</span><span style="color:#B392F0"> data-drawer-close</span><span style="color:#E1E4E8">>Apply Filters</</span><span style="color:#85E89D">button</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> </</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"></</span><span style="color:#FDAEB7;font-style:italic">Drawer</span><span style="color:#E1E4E8">></span></span></code></pre>Settings Panel
User preferences and toggles:
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="html"><code><span class="line"><span style="color:#E1E4E8"><</span><span style="color:#FDAEB7;font-style:italic">Drawer</span><span style="color:#B392F0"> id</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"settings"</span><span style="color:#B392F0"> label</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"Settings"</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"p-6 flex flex-col gap-4"</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"flex items-center justify-between"</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">span</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"text-sm"</span><span style="color:#E1E4E8">>Notifications</</span><span style="color:#85E89D">span</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">input</span><span style="color:#B392F0"> type</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"checkbox"</span><span style="color:#B392F0"> checked</span><span style="color:#E1E4E8"> /></span></span> <span class="line"><span style="color:#E1E4E8"> </</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> </span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"flex items-center justify-between"</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">span</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"text-sm"</span><span style="color:#E1E4E8">>Dark Mode</</span><span style="color:#85E89D">span</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">input</span><span style="color:#B392F0"> type</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"checkbox"</span><span style="color:#E1E4E8"> /></span></span> <span class="line"><span style="color:#E1E4E8"> </</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span> <span class="line"></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">hr</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"border-slate-200"</span><span style="color:#E1E4E8"> /></span></span> <span class="line"></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">button</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"btn-secondary w-full"</span><span style="color:#B392F0"> data-drawer-close</span><span style="color:#E1E4E8">>Close</</span><span style="color:#85E89D">button</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> </</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"></</span><span style="color:#FDAEB7;font-style:italic">Drawer</span><span style="color:#E1E4E8">></span></span></code></pre>Slot
The default slot renders inside the slide-in panel. Style it however you want — the panel itself only provides the positioning, sizing, transition, and scroll container.
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8; overflow-x: auto;" tabindex="0" data-language="astro"><code><span class="line"><span style="color:#E1E4E8"><</span><span style="color:#79B8FF">Drawer</span><span style="color:#B392F0"> id</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"filters"</span><span style="color:#B392F0"> label</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"Product filters"</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">div</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"p-6 flex flex-col gap-4"</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"> <</span><span style="color:#85E89D">h2</span><span style="color:#B392F0"> class</span><span style="color:#E1E4E8">=</span><span style="color:#9ECBFF">"text-lg font-textbold"</span><span style="color:#E1E4E8">>Filters</</span><span style="color:#85E89D">h2</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#6A737D"> <!-- your form here --></span></span> <span class="line"><span style="color:#E1E4E8"> </</span><span style="color:#85E89D">div</span><span style="color:#E1E4E8">></span></span> <span class="line"><span style="color:#E1E4E8"></</span><span style="color:#79B8FF">Drawer</span><span style="color:#E1E4E8">></span></span></code></pre>