Drawer

A headless, accessible slide-out drawer panel with focus trapping, scroll lock, and directional positioning.

Features

  • Directional slide
  • Focus trapping
  • Focus restoration
  • Scroll lock
  • Escape key dismissal
  • Controlled or uncontrolled open state
  • Headless styling

Installation

npm install @ariaui/drawer

Examples

Slide-out panel with scroll lock and focus trap. Directional positioning props plus Tailwind on className.

Slide-out drawer

A right-aligned drawer with form fields, footer actions, and a dismiss button.

Preview

No preview output yet.

live.tsxReady

Drawer sides

Four drawer triggers that open panels from the top, right, bottom, and left side of the viewport.

Preview

No preview output yet.

live.tsxReady

Framer Motion

Four side drawers using Framer Motion through Overlay and Content asChild composition.

Preview

No preview output yet.

live.tsxReady

Anatomy

tsx
import * as Drawer from "@ariaui/drawer";

export default function Example() {
  return (
    <Drawer.Root>
      <Drawer.Trigger />
      <Drawer.Portal>
        <Drawer.Overlay />
        <Drawer.Content>
          <Drawer.Header>
            <Drawer.Title />
            <Drawer.Description />
          </Drawer.Header>
          <Drawer.Footer>
            <Drawer.Cancel />
            <Drawer.Action />
            <Drawer.Close />
          </Drawer.Footer>
        </Drawer.Content>
      </Drawer.Portal>
    </Drawer.Root>
  );
}

API Reference

Root

State container. Owns the open/closed state and provides context to all child components.

PropTypeDefault
open
boolean
defaultOpen
booleanfalse
onOpenChange
(open: boolean) => void

Trigger

Button that opens the drawer. Acts as the focus restoration target when the drawer closes.

AttributeValues
aria-haspopupdialog
aria-expanded"true" when open, "false" when closed
aria-controlsID of the Content element
[data-state]"open" | "closed"

Portal

Portals the overlay and content to document.body by default.

PropTypeDefault
container
HTMLElementdocument.body

Overlay

Background backdrop behind the drawer content. Clicking it closes the drawer.

PropTypeDefault
asChild
booleanfalse
AttributeValues
[data-state]"open" | "closed"

Content

Drawer panel with dialog ARIA semantics, focus trapping, Escape dismissal, and body scroll lock.

PropTypeDefault
side
"top" | "right" | "bottom" | "left""bottom"
asChild
booleanfalse
AttributeValues
roledialog
aria-modaltrue
aria-labelledbyID of the Title element
aria-describedbyID of the Description element
[data-state]"open" | "closed"
[data-side]"top" | "right" | "bottom" | "left"

Title

Accessible title for the drawer. Referenced by Content via aria-labelledby.

Description

Accessible description for the drawer. Referenced by Content via aria-describedby.

Close

Button that closes the drawer when clicked. Use for auxiliary close actions (e.g., X icon).

Cancel

Cancel button that closes the drawer when clicked. Use for secondary/destructive cancel actions.

AttributeValues
data-drawer-cancelPresent when rendered

Action

Action button that calls the onClick handler, then closes the drawer (unless preventDefault is called). Use for primary confirmation actions.

AttributeValues
data-drawer-actionPresent when rendered

Keyboard

ShortcutAction
EscClose the drawer and return focus to the trigger.
TabMove focus to the next focusable element within the drawer (loops to first).
Shift+TabMove focus to the previous focusable element within the drawer (loops to last).
Enter/SpaceActivate the focused button (trigger, close, or any action button).

Accessibility

The Drawer component implements the WAI-ARIA Dialog (Modal) pattern adapted for slide-out panels:

  • Content renders with role="dialog" and aria-modal="true" to mark the surface as modal.
  • Content is labelled via aria-labelledby pointing to Title and aria-describedby pointing to Description.
  • Trigger exposes aria-haspopup="dialog", aria-expanded, and aria-controls for assistive technology.
  • Focus is trapped within the drawer using FocusScope — Tab loops through focusable elements.
  • Escape closes the drawer from any focusable element inside it.
  • Focus is restored to the trigger element when the drawer closes.
  • Body scroll is locked while the drawer is open to prevent interaction with underlying content.
Previous
Disclosure