Skip to content
View as Markdown

Disclosures

Drawer

An edge-anchored panel that slides in over the page, built on the native <dialog> element and the browser's top layer.

Import

import { Drawer } from "@loamui/core";

Usage

Every example has a CSStab. That’s the real, complete stylesheet for the component: plain, static CSS, with nothing running in the browser.

Basic usage

Compose the drawer from parts. The Panel is a native <dialog> opened with showModal(): top layer, backdrop, focus containment, Escape and focus restore all come from the browser. A Drawer is a Modal pinned to an edge with a slide transition.

Navigation

Jump to a section of the app.

Sides

side anchors the panel to an edge and picks the slide direction. It is logical (start / end / top / bottom), so start and end follow the writing mode: inline-start is the left in LTR, the right in RTL.

Side: start

start/end set the panel width; top/bottom set its height. Both follow writing mode.

Side: end

start/end set the panel width; top/bottom set its height. Both follow writing mode.

Side: top

start/end set the panel width; top/bottom set its height. Both follow writing mode.

Side: bottom

start/end set the panel width; top/bottom set its height. Both follow writing mode.

Sizes

size sets the panel's short axis: the width for start / end, the height for top / bottom. The long axis always fills the viewport.

A sm drawer

The panel width comes from the size prop.

A md drawer

The panel width comes from the size prop.

A lg drawer

The panel width comes from the size prop.

Header with a close button

A header row with an × is a composition pattern, not configuration: compose Drawer.Title and Drawer.Close however your design needs.

Filters

Refine the results shown in the list.

Guidance

When to use it

  • For navigation and secondary content that shouldn't take over the whole screen: a mobile menu, a filter panel, a details side-sheet.
  • When the surrounding page context should stay visible behind a dimmed backdrop, and the panel is casually dismissible.
  • On narrow screens, to hold navigation that sits in a persistent sidebar on wider ones.

When not to

  • For a focused, must-answer task or a destructive confirmation, use Modal, a dialog centred on the decision, not parked at an edge.
  • For a small set of actions opened from a control, use Menu; for supplementary content anchored to a trigger, use Popover.
  • For primary navigation that fits on wide screens, use a persistent sidebar or nav bar rather than hiding it behind a trigger.

Accessibility

  • Built on the native <dialog> opened with showModal(): the browser provides the top layer, ::backdrop, real focus containment, Escape handling, and restores focus to the trigger on close, none of it re-implemented in JavaScript.
  • Drawer.Title and Drawer.Description automatically label and describe the dialog via aria-labelledby / aria-describedby.
  • Light dismiss (clicking the backdrop) uses the closedby attribute where supported, with a small feature-detected coordinate-check fallback elsewhere: no polyfills, per the browser support policy.
  • The slide transition lives inside prefers-reduced-motion: no-preference, so users who ask for reduced motion get an instant open with no movement.
  • A forced-colors border keeps the panel edge visible when background colours are overridden; body scroll is locked while open.

How it works

Anchor to a logical edge

side is logical, not physical: start and end follow the writing mode so a start drawer opens from the left in LTR and the right in RTL, matching where users expect the back edge to be. Reach for top / bottom for sheets: a bottom sheet is the thumb-friendly choice for actions on touch devices.

A drawer is dismissible by nature

Unlike an alert dialog, a drawer light-dismisses: clicking the backdrop or pressing Escape closes it. That is the right contract for navigation and browsing. Never put a decision the user must not dismiss by accident in a drawer; use Modal's alert for that.

Always render a Title

Drawer.Title labels the dialog via aria-labelledby; it is what screen readers announce on open. A drawer used purely for navigation should still carry a Title (for example "Navigation") so its purpose is announced, even if you hide it visually.

Keep the trigger where the focus returns

The browser restores focus to the trigger when the drawer closes. Keep a single, stable trigger (a hamburger button) rather than swapping it out while open, so keyboard and screen-reader users land back where they started.

Parts

Drawer.Root

Groups the parts and owns open state (controlled or uncontrolled). Renders no element of its own.

PropTypeDefaultDescription
openbooleanControlled open state.
defaultOpenbooleanfalseUncontrolled initial state.
onOpenChange(open: boolean) => voidFires when the drawer opens or closes.

Drawer.Trigger

A LoamUI Button that opens the drawer.

PropTypeDefaultDescription
renderRenderPropSubstitute your own action element (an icon button); the wiring merges on.
...othersButtonHTMLAttributesForwarded to the button.

Drawer.Panel

The native <dialog>. side anchors it to an edge and sets the slide direction; size sets the short-axis extent (width for start/end, height for top/bottom).

PropTypeDefaultDescription
side"start" | "end" | "top" | "bottom""start"Edge to anchor to.
size"sm" | "md" | "lg""md"Short-axis extent.

Drawer.Title / Drawer.Description

Label and describe the dialog for assistive technology.

Drawer.Close

A LoamUI Button that closes the drawer; compose as many as you need (a footer action, a header ×). Supports render like the Trigger.