Drawer
A native <dialog class=
that slides in from an edge of the window.
data-side="right" slides in from the right edge
The parts are the same as a dialog's:
<header>, <div> (scrolls)
and <footer>. Open and close it like a dialog,
with commandfor and command. A side
drawer is the full height of the window and 24rem wide.
<button type="button" class="btn" commandfor="drw-filter" command="show-modal"><i class="icon icon-list-filter" aria-hidden="true"></i>Filters</button>
<dialog class="drawer" id="drw-filter" data-side="right" aria-labelledby="drw-filter-title" closedby="any">
<header>
<h2 id="drw-filter-title">Filters</h2>
<button type="button" class="btn" data-variant="ghost" data-size="icon" commandfor="drw-filter" command="close" aria-label="Close"><i class="icon icon-x" aria-hidden="true"></i></button>
</header>
<div class="stack">
<div class="field">
<label for="drw-public">Is public</label>
<select id="drw-public">
<option>All</option>
<option>Yes</option>
<option>No</option>
</select>
</div>
<div class="field">
<label for="drw-gift">Is gift card</label>
<select id="drw-gift">
<option>All</option>
<option>Yes</option>
<option>No</option>
</select>
</div>
</div>
<footer>
<button type="button" class="btn" commandfor="drw-filter" command="close">Cancel</button>
<button type="button" class="btn" data-variant="primary" commandfor="drw-filter" command="close">Search</button>
</footer>
</dialog>
data-side="left" slides in from the left edge
<button type="button" class="btn" commandfor="drw-menu" command="show-modal"><i class="icon icon-menu" aria-hidden="true"></i>Menu</button>
<dialog class="drawer" id="drw-menu" data-side="left" aria-labelledby="drw-menu-title" closedby="any">
<header>
<h2 id="drw-menu-title">Menu</h2>
<button type="button" class="btn" data-variant="ghost" data-size="icon" commandfor="drw-menu" command="close" aria-label="Close"><i class="icon icon-x" aria-hidden="true"></i></button>
</header>
<div>
<nav class="item-group" aria-label="Store">
<a class="item" data-size="sm" href="#"><figure><i class="icon icon-house" aria-hidden="true"></i></figure><section><h3>Home</h3></section></a>
<a class="item" data-size="sm" href="#"><figure><i class="icon icon-package" aria-hidden="true"></i></figure><section><h3>Products</h3></section></a>
<a class="item" data-size="sm" href="#"><figure><i class="icon icon-shopping-bag" aria-hidden="true"></i></figure><section><h3>Orders</h3></section></a>
<a class="item" data-size="sm" href="#"><figure><i class="icon icon-user" aria-hidden="true"></i></figure><section><h3>Customers</h3></section></a>
</nav>
</div>
</dialog>
data-side="bottom", or no data-side, slides up from the bottom edge
The default edge is the bottom, as in Tsugi. Top and bottom drawers are the full width of the window, at most 85% of its height, and keep their content in a centred 32rem column; their footer buttons fill that column, the main one first.
<button type="button" class="btn" commandfor="drw-delete" command="show-modal">Delete products</button>
<dialog class="drawer" id="drw-delete" aria-labelledby="drw-delete-title" closedby="any">
<header>
<h2 id="drw-delete-title">Delete 3 products?</h2>
<p>They are removed from every category and campaign. This cannot be undone.</p>
</header>
<footer>
<button type="button" class="btn" data-variant="danger" commandfor="drw-delete" command="close">Delete products</button>
<button type="button" class="btn" commandfor="drw-delete" command="close">Cancel</button>
</footer>
</dialog>
data-side="top" slides down from the top edge
<button type="button" class="btn" commandfor="drw-notice" command="show-modal">Announcement</button>
<dialog class="drawer" id="drw-notice" data-side="top" aria-labelledby="drw-notice-title" closedby="any">
<header>
<h2 id="drw-notice-title">Scheduled maintenance</h2>
<p>Payments are paused on Sunday from 2:00 to 2:30 a.m.; orders placed then are captured afterwards.</p>
</header>
<footer>
<button type="button" class="btn" data-variant="primary" commandfor="drw-notice" command="close">Got it</button>
</footer>
</dialog>
close() takes a value, and the page reads it from the close event
A command= button with a
value passes that value as returnValue,
like calling drawer. in JS.
Read returnValue in the close event,
then clear it: the browser keeps it for the next opening. Escape
or a backdrop click leaves it empty. The slide-out runs before the
dialog is gone; with reduced motion it closes at once.
Nothing chosen yet.
<div class="stack" data-gap="sm">
<div><button type="button" class="btn" commandfor="drw-export" command="show-modal">Export orders</button></div>
<p id="drw-export-result">Nothing chosen yet.</p>
</div>
<dialog class="drawer" id="drw-export" aria-labelledby="drw-export-title" closedby="any">
<header>
<h2 id="drw-export-title">Export orders</h2>
<p>Choose the file format.</p>
</header>
<footer>
<button type="button" class="btn" data-variant="primary" commandfor="drw-export" command="close" value="csv">CSV</button>
<button type="button" class="btn" commandfor="drw-export" command="close" value="xlsx">Excel</button>
</footer>
</dialog>
<script type="module">
const drawer = document.getElementById("drw-export");
drawer.addEventListener("close", () => {
document.getElementById("drw-export-result").textContent =
drawer.returnValue === "" ? "Closed without a choice." : `Chosen: ${drawer.returnValue}`;
drawer.returnValue = "";
});
</script>
API
Methods and properties (native)
| Member | Type | Description |
|---|---|---|
showModal(
|
method |
Opens the drawer over the dimmed page; what
command= calls.
|
close(
|
method |
Closes it with its slide-out; the optional string becomes
returnValue.
|
returnValue |
property |
The value from close( or from
the button's value. Empty after Escape; not
reset on open.
|
Events (native)
| Event | Detail | Description |
|---|---|---|
close |
none |
Sent once the drawer has closed. Read
returnValue here.
|
cancel |
none |
Before Escape or a backdrop click closes it.
preventDefault( keeps it open.
|
Keyboard
| Key | Description |
|---|---|
| Esc | Closes the drawer, with its slide-out. |
| Tab | Moves between the controls of the drawer only. |
No script: the slide uses CSS transitions, turned off by
prefers-reduced-motion. A browser without discrete
transitions shows and hides it at once.
Attributes
| Attribute / element | Values | Description |
|---|---|---|
class=
|
on <dialog> |
The component. |
data-
|
none or bottom (default), top,
left, right
|
The edge it slides in from. Left and right: full height, 24rem wide. Top and bottom: full width, up to 85% of the height. |
aria-
|
id of the <h2> |
Names the drawer. Required. |
closedby |
any, closerequest (default),
none
|
any: Escape and a backdrop click close it.
Same as the dialog.
|
commandfor + command |
drawer id + show-,
close
|
On a button: opens or closes the drawer. |
value |
text, on a command= button
|
Becomes the drawer's
returnValue.
|
> header |
h2, p, close button
|
The close button sits at the end of the first row. |
> div |
any content | Fills the height and scrolls. |
> footer |
buttons | Side drawers: at the end. Top and bottom drawers: stacked, full width. |