Skip to main content

Drawer

A controlled overlay panel that slides from an edge of the viewport. Implements focus trapping, scroll locking, inert background handling, escape key support, and animated mount/unmount lifecycle.


Usage

Basic (Right side)

import { Drawer } from '@nofinite/nui';
const [open, setOpen] = useState(false);

<>
<button onClick={() => setOpen(true)}>Open</button>
<Drawer open={open} onClose={() => setOpen(false)}>
Drawer content
</Drawer>
</>;

Left position

<Drawer open={open} onClose={close} position="left">
Sidebar content
</Drawer>

Bottom sheet

<Drawer open={open} onClose={close} position="bottom">
Mobile sheet content
</Drawer>

Disable Escape + Outside click

<Drawer open={open} onClose={close} disableEsc disableClickOutside>
Forced interaction
</Drawer>

Props

PropTypeDefaultDescription
openbooleanControls visibility
onClose() => voidTriggered on escape or outside click
position'left' | 'right' | 'top' | 'bottom'rightDrawer origin
disableEscbooleanfalseDisables Escape key close
disableClickOutsidebooleanfalseDisables overlay click close
overlayClassNamestringOverlay class override
classNamestringDrawer class override
childrenReactNodeDrawer content

Extends React.HTMLAttributes<HTMLDivElement>.


Subcomponents

None exposed.


Variants

Drawer supports structural and behavioral variants.

<Drawer position="right" />
<Drawer position="left" />
<Drawer position="top" />
<Drawer position="bottom" />
<Drawer disableEsc />
<Drawer disableClickOutside />

Available variants:

  • right (default)
  • left
  • top
  • bottom
  • escape-disabled
  • outside-click-disabled

Guidelines:

  • Use right/left for navigation or contextual panels
  • Use bottom for mobile sheet patterns
  • Avoid disabling both escape and outside click unless required by workflow

Sizes

Drawer width/height is position-dependent:

PositionDefault Dimension
left/rightmax-width: 320px
top/bottommin-height: 250px, max-height: 90vh

Width/height should be customized via className and layout utilities.


Shapes / Modes

Positions

  • Left
  • Right
  • Top
  • Bottom

States

  • Mounted (in DOM)
  • Visible (animated open)
  • Closed (animated exit)
  • Escape-triggered close
  • Outside-click close
  • Scroll-locked background
  • Focus-trapped
  • Inert-applied siblings

Design tokens / theming

Uses NUI tokens:

  • --nui-bg-surface
  • --nui-fg-default
  • --nui-border-default
  • --nui-radius-xl
  • Z-index stack:
    • Overlay: 9998
    • Drawer: 9999

Overlay uses backdrop blur + 0.5 opacity.


Accessibility

Implemented behavior:

  • role="dialog"
  • aria-modal="true"
  • Focus trapped while open
  • Previous focus restored on close
  • Escape key close (configurable)
  • Background content inert while open
  • Scroll lock applied to body
  • Click outside detection (configurable)
  • prefers-reduced-motion respected

Note: Overlay is aria-hidden="true" because it is non-interactive.


Animation Model

Two-phase state system:

  • isMounted → Controls DOM presence
  • isVisible → Controls CSS transition state

Exit animation delay: 300ms
Entrance delay before activation: ~15ms (transition trigger)

Uses GPU-accelerated transform transitions.


Best practices

Do

  • Control open state from parent
  • Use semantic heading inside drawer
  • Provide a visible close button
  • Keep drawer width constrained

Don’t

  • Use Drawer for full-screen modal (use Modal)
  • Disable escape without strong reason
  • Mount heavy trees inside if frequent toggling
  • Stack multiple drawers without z-index management