Skip to content

Drawer

Open a panel from a screen edge and swipe it away.

import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use Drawer for action sheets, pickers and short forms on touch screens. Like Sheet, it is modal, with the addition of swipe dismissal. For mouse-driven layouts, consider a Dialog, Sheet or Popover.

import {
  Button,
  Drawer,
  DrawerClose,
  DrawerContent,
  DrawerDescription,
  DrawerFooter,
  DrawerHeader,
  DrawerTitle,
  DrawerTrigger,
} from '@youj/react'

export function DeploymentActions() {
  return (
    <Drawer>
      <DrawerTrigger render={<Button variant="outline" />}>Actions</DrawerTrigger>
      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>Preview deployment</DrawerTitle>
          <DrawerDescription>feat/search, built in 41s.</DrawerDescription>
        </DrawerHeader>
        <DeploymentActionList />
        <DrawerFooter>
          <DrawerClose render={<Button variant="outline" />}>Cancel</DrawerClose>
        </DrawerFooter>
      </DrawerContent>
    </Drawer>
  )
}

DrawerContent renders the portal, the scrim, a viewport and the panel. A top or bottom drawer is a centered column up to 36rem wide, full width on a phone, with a grab handle. Its footer stacks actions at full width, as touch sheets do. Drawer is Base UI's Drawer root, so open, onOpenChange, modal and swipeDirection work as Base UI documents them.

Reference

PropsshowHandle (1)

Props

DrawerContent adds one prop to Base UI's Drawer.Popup.

DrawerContent props
PropType
showHandleboolean

The grab handle on a top or bottom drawer.

default true

true
Other props pass through: extends DrawerPrimitive.Popup.Props.

Drawer sets swipeDirection to down by default and passes every other Base UI root prop through. DrawerTrigger, DrawerClose, DrawerTitle and DrawerDescription pass their props through to Base UI. DrawerHeader and DrawerFooter are divs.

MotionEnter, exit, swipe… · reduced motion

Motion

Motion tokens
MomentTokens
Enterslides in from its edge (yj-sheet-*)
duration.slowease.enter

340ms · cubic-bezier(0.16, 1, 0.3, 1)

340mscubic-bezier(0.16, 1, 0.3, 1)
Exitslides off its edge; a flick shortens it
duration.baseease.exit

220ms · cubic-bezier(0.4, 0, 1, 1)

220mscubic-bezier(0.4, 0, 1, 1)
Swipefollows the finger exactly
no token

no transition while dragging

no transition while dragging
Scrim inopacity (yj-backdrop)
duration.baseease.standard

220ms · cubic-bezier(0.2, 0, 0, 1)

220mscubic-bezier(0.2, 0, 0, 1)
Scrim outthins with the swipe progress, and leaves faster after a flick
duration.fastease.standard

140ms · cubic-bezier(0.2, 0, 0, 1)

140mscubic-bezier(0.2, 0, 0, 1)

Reduced motion

Opens and closes instantly. A swipe still tracks the finger, because that motion is the person's own, and a released swipe dismisses at once.

After a swipe, the exit duration is multiplied by the swipe's strength, a factor from 0.1 to 1 that Base UI derives from how fast the panel was released. A hard flick leaves sooner than a slow drag that is let go.

Reduced motion

The drawer appears and leaves without sliding, and the scrim with it. Dragging still moves the panel under the finger, because that movement is direct manipulation, not animation. Everything else, from focus to dismissal, is unchanged.

AccessibilityKeyboard, ARIA, focus

Accessibility

  • A drawer is a modal dialog: role="dialog", named by DrawerTitle and described by DrawerDescription.
  • Swiping is never the only way out. Escape, a press on the scrim and any DrawerClose close it too, and focus returns to the trigger.
  • Focus moves into the drawer when it opens and is trapped there until it closes.
  • The grab handle is decorative (aria-hidden). When the panel itself scrolls with nothing focusable inside, it becomes a tab stop so the keyboard can scroll it.