Drawer
A drawer is a native <dialog> with .re-drawer added alongside .re-dialog.
It opens with the same dialog wiring —
enhanceDialog and the data-re-dialog-trigger / -target / -close /
-close-on-backdrop attributes — so the browser owns the top layer, backdrop,
focus trap, Escape, and inert background. This file only changes the box
geometry and adds a slide-in.
import { enhanceDialog } from "@relements/core/behaviors/dialog";enhanceDialog(document);Set the edge with data-side: end (default), start, top, bottom —
pinned with logical insets, so start/end follow the writing direction. Size
along the pin axis with data-size="sm | md | lg". Give every drawer an
accessible name (aria-labelledby → its .re-dialog__title, or aria-label).
The slide uses @starting-style + allow-discrete and degrades to an instant
show/hide on engines without them (and under prefers-reduced-motion).
Dismissal is Escape or backdrop only — there’s no swipe gesture.
<div class="drawer-triggers">
<button
type="button"
class="re-button"
data-re-dialog-trigger
data-re-dialog-target="drawer-end"
>
End
</button>
<button
type="button"
class="re-button"
data-variant="secondary"
data-re-dialog-trigger
data-re-dialog-target="drawer-start"
>
Start
</button>
<button
type="button"
class="re-button"
data-variant="secondary"
data-re-dialog-trigger
data-re-dialog-target="drawer-top"
>
Top
</button>
<button
type="button"
class="re-button"
data-variant="secondary"
data-re-dialog-trigger
data-re-dialog-target="drawer-bottom"
>
Bottom
</button>
</div>
<dialog
class="re-dialog re-drawer"
id="drawer-end"
data-side="end"
aria-labelledby="drawer-end-title"
data-re-dialog-close-on-backdrop
>
<header class="re-dialog__header">
<h2 class="re-dialog__title" id="drawer-end-title">Settings</h2>
<button class="re-dialog__close" aria-label="Close" data-re-dialog-close value="close">
×
</button>
</header>
<div class="re-dialog__body">
<p>Drawer pinned to the inline-end edge. Scrolls independently when tall.</p>
</div>
<footer class="re-dialog__footer">
<button
type="button"
class="re-button"
data-variant="ghost"
data-re-dialog-close
value="cancel"
>
Cancel
</button>
<button type="button" class="re-button" data-re-dialog-close value="save">Save</button>
</footer>
</dialog>
<dialog
class="re-dialog re-drawer"
id="drawer-start"
data-side="start"
aria-labelledby="drawer-start-title"
data-re-dialog-close-on-backdrop
>
<header class="re-dialog__header">
<h2 class="re-dialog__title" id="drawer-start-title">Navigation</h2>
<button class="re-dialog__close" aria-label="Close" data-re-dialog-close value="close">
×
</button>
</header>
<div class="re-dialog__body"><p>Drawer pinned to the inline-start edge.</p></div>
</dialog>
<dialog
class="re-dialog re-drawer"
id="drawer-top"
data-side="top"
aria-labelledby="drawer-top-title"
data-re-dialog-close-on-backdrop
>
<header class="re-dialog__header">
<h2 class="re-dialog__title" id="drawer-top-title">Notifications</h2>
<button class="re-dialog__close" aria-label="Close" data-re-dialog-close value="close">
×
</button>
</header>
<div class="re-dialog__body"><p>Sheet pinned to the block-start edge.</p></div>
</dialog>
<dialog
class="re-dialog re-drawer"
id="drawer-bottom"
data-side="bottom"
aria-labelledby="drawer-bottom-title"
data-re-dialog-close-on-backdrop
>
<header class="re-dialog__header">
<h2 class="re-dialog__title" id="drawer-bottom-title">Filters</h2>
<button class="re-dialog__close" aria-label="Close" data-re-dialog-close value="close">
×
</button>
</header>
<div class="re-dialog__body">
<p>Bottom sheet. Dismissal is Escape or backdrop — no swipe gesture.</p>
</div>
</dialog> Accessibility
Section titled “Accessibility”A drawer is a native modal <dialog> opened with showModal(), so its
accessibility comes straight from the platform — the same as
dialog.
- Keyboard — opens from the trigger button (Enter/Space). The browser traps
focus inside the open panel: Tab and Shift+Tab cycle through its controls
without leaving. Escape dismisses it (along with a backdrop click when
data-re-dialog-close-on-backdropis set). There is no swipe gesture. - Focus — on open, focus moves into the panel and the background goes
inert; on close it returns to the trigger — all native<dialog>behavior. Interactive controls show the standard:focus-visiblering (the close button uses--re-shadow-focus). - Semantics — the
<dialog>is announced as a modal dialog. Give every drawer an accessible name:aria-labelledbypointing at its.re-dialog__title, oraria-label. The icon close button carriesaria-label="Close".data-sideanddata-sizeare presentation only and add no semantics. - Notes — the slide-in and backdrop fade collapse to an instant show/hide
under
prefers-reduced-motionand on engines without@starting-style/allow-discrete. See the accessibility guide.