Drawer

Side-anchored panels for detail without losing place.

v1.0.0·Updated Jul 8, 2026 · 10:45
Live Demo — Fully Interactive
Detail beside the list, not on top of it
Configuration
Side
Width
Footer
Title
Button · v3.2
Panel subtitle
Detail content lives here, scrolling on its own.
CancelSave
<Drawer side="right" title="Button · v3.2" actions={["Cancel", "Save"]} />
Behavior — The Engineering Contract
Props
PropTypeDefaultDescription
openbooleanfalseControlled visibility
onClose(reason) => voidEscape, scrim click, or close button
sideright · leftrightWhich edge it slides from
widthnarrow · default · widedefaultPanel width; caps at 80% of the viewport
titlestringLabels the panel for assistive tech
dismissablebooleantrueWhen false, only an action closes it
Events & keyboard
TriggerResult
EscapeCloses the drawer
Scrim clickCloses; clicks inside the panel do not
Tab / Shift+TabFocus is trapped within the panel
On open / closeFocus moves in, then back to the trigger

Reach for a drawer over a modal when the user needs the page's context to stay put — editing a row while the list stays visible, or reading detail without losing scroll position. A modal interrupts; a drawer accompanies.

Anatomy
1
2345
Details
Subtitle
Scrolling content.
Save
1ScrimDims the page and catches the dismiss click, while the context stays visible behind it.
2PanelFull-height sheet anchored to one edge, sliding in from off-screen.
3HeaderTitle, optional subtitle, and a close button pinned to the top.
4BodyGrows to fill and scrolls independently of the page beneath.
5FooterSticky actions with a hairline top, so Save is always reachable.
Do's and Don'ts
✓ Do
List stays visible · drawer edits a row

Use a drawer when keeping the page's context in view helps — editing beside the list.

✕ Don't
Yes / No confirm in a wide drawer

Don't use a drawer for a single yes/no. That interruption belongs to a modal.

✓ Do
CancelSave

Keep the primary action sticky in the footer, always reachable however long the body scrolls.

✕ Don't
Drawer opening a second drawer

Never stack drawers on drawers. If a step needs another step, it wants a full page.

In context: Examples
A · Detail beside a list
Row detail
Read without losing your place.

The list stays put on the left; detail arrives on the right. No context lost.

B · Left navigation
OverviewComponentsTokens

On small screens the drawer becomes the primary navigation, sliding from the left.

C · Edit form
Edit component
Name…
Save

A few fields with a sticky Save — longer than a modal wants, shorter than a page.

Measurements & Tokens
TokenValueRole
--drawer-wmin(320px, 80%)Panel width, capped to the viewport
--drawer-bg#101114Panel surface
--drawer-border#23242AInner-edge hairline
--drawer-shadow-18px 0 50px rgba(0,0,0,.5)Cast toward the page (flips for left)
--scrimrgba(4,4,6,.62)Backdrop dim
--drawer-foot-border#1E1F24Sticky footer hairline
--drawer-easecubic-bezier(.22,1,.36,1)Slide, 0.3s
Code
<div class="o-drawer-scrim is-open">
  <aside class="o-drawer o-drawer--right" role="dialog" aria-modal="true">
    <div class="o-drawer__head">
      <div>
        <h2 class="o-drawer__title">Button · v3.2</h2>
        <p class="o-drawer__sub">Forms · 8 variants</p>
      </div>
      <button class="o-drawer__close" aria-label="Close">✕</button>
    </div>
    <div class="o-drawer__body"><!-- scrolls --></div>
    <div class="o-drawer__foot">
      <button class="o-btn o-btn--ghost">Cancel</button>
      <button class="o-btn o-btn--primary">Save</button>
    </div>
  </aside>
</div>