RoadieRoadie
  • Home
  • Foundations
  • Tokens
  • Components
  • Charts
  • Widgets
  • Appearance
HomeFoundationsComponents
Appearance

Components

  • Navigation

    • Breadcrumb
    • Navigator
    • Steps
    • Tabs
  • Layout

    • Pane
    • ScrollArea
    • Separator
  • Actions

    • Button
    • IconButton
    • Toggle
    • Toggle group
  • Forms

    • Forms overview
    • Autocomplete
    • Checkbox
    • Combobox
    • Field
    • Fieldset
    • Input
    • Label
    • Number field
    • OTP field
    • Radio group
    • Select
    • Slider
    • Switch
    • Textarea
  • Overlays

    • Dialog
    • Drawer
    • Menu
    • Popover
    • Toast
    • Tooltip
  • Collections

    • Accordion
    • Card
    • Carousel
    • Collapsible
    • DataTable
    • List
    • Marquee
    • Table
  • Status

    • Badge
    • Callout
    • Countdown
    • Empty state
    • Meter
    • Progress
    • Skeleton
    • Sparkline
    • StatTile
  • Media & brand

    • Avatar
    • Icon Tile
    • Image
    • Logo
    • QR code
    • SpotIllustration
  • Text

    • CalendarTile
    • Code
    • DateTime
    • Duration
    • Highlight
    • Mark
    • Prose

Drawer

A surface that slides in from an edge and swipes away, built on Base UI's drawer primitive.

Import

import { Drawer } from '@oztix/roadie-components/drawer'

Examples

Default

A bottom sheet. Drawer.Content collapses the underlying Portal, Backdrop, Viewport and Popup into one element, and adds the grab handle because the swipe axis is vertical.

<Drawer>
  <Drawer.Trigger render={<Button>Switch organisation</Button>} />
  <Drawer.Content>
    <Drawer.Header>
      <Drawer.Title>Organisations</Drawer.Title>
      <Drawer.Description>Switch the organisation you're working in.</Drawer.Description>
    </Drawer.Header>
    <Drawer.Body>
      <List emphasis='subtler'>
        {[
          { name: 'Paperbark Presents', members: 12, current: true },
          { name: 'The Corner Room', members: 4 },
          { name: 'Saltwater Sessions', members: 1 }
        ].map((org) => (
          <List.Item
            key={org.name}
            title={org.name}
            description={`${org.members} ${org.members === 1 ? 'member' : 'members'}`}
            current={org.current}
            chevron={false}
            leading={
              <IconTile intent='accent'>
                <BuildingsIcon weight='bold' />
              </IconTile>
            }
            trailing={org.current ? <CheckIcon weight='bold' className='size-5' /> : undefined}
          />
        ))}
      </List>
    </Drawer.Body>
  </Drawer.Content>
</Drawer>

Drawer.Content publishes --content-inset, so its header, body and footer share one horizontal inset. Override the variable on Drawer.Content to change all three.

Sides

side picks the edge, and with it the dismiss gesture. A bottom drawer swipes down. A right drawer swipes right. It is a single value, not a breakpoint list: Base UI owns the gesture in JavaScript, so the edge and the swipe have to agree.

const addOns = [
  { title: 'Parking', description: 'Car park B, all day', price: '$25.00' },
  { title: 'Camping', description: 'Unpowered site, two nights', price: '$60.00' },
  { title: 'Festival tee', description: 'Collect at the merch tent', price: '$45.00' }
]

const sides = {
  bottom: {
    label: 'Add ons',
    title: 'Add ons',
    description: 'Paperbark Sessions · Sat 14 Nov',
    body: (
      <List emphasis='subtler'>
        {addOns.map((addOn) => (
          <List.Item key={addOn.title} title={addOn.title} description={addOn.description} chevron={false} trailing={<span className='font-semibold'>{addOn.price}</span>} />
        ))}
      </List>
    ),
    footer: <Button intent='accent' emphasis='strong' className='w-full'>Continue</Button>
  },
  right: {
    label: 'Order details',
    title: 'Order 10482',
    description: 'Placed Tue 3 Sep · 3 items',
    body: (
      <List emphasis='subtler'>
        <List.Item title='General admission' description='2 × $68.00' chevron={false} trailing='$136.00' />
        <List.Item title='Parking' description='1 × $25.00' chevron={false} trailing='$25.00' />
        <List.Item title='Booking fee' chevron={false} trailing='$6.50' />
      </List>
    ),
    footer: <Button className='w-full'>Resend tickets</Button>
  },
  left: {
    label: 'Menu',
    title: 'Menu',
    body: (
      <List emphasis='subtler'>
        <List.Item title='Home' leading={<HouseIcon weight='bold' className='size-5' />} current />
        <List.Item title='Tickets' leading={<TicketIcon weight='bold' className='size-5' />} />
        <List.Item title='Account' leading={<UserCircleIcon weight='bold' className='size-5' />} />
      </List>
    )
  },
  top: {
    label: 'Search',
    title: 'Search',
    body: (
      <div className='grid gap-4 pb-4'>
        <Field>
          <Field.Label>Search events</Field.Label>
          <Field.Input type='search' placeholder='Artists, venues and events' />
        </Field>
        <List emphasis='subtler'>
          <List.Item title='Paperbark Sessions' description='Recent search' />
          <List.Item title='The Corner Room' description='Recent search' />
        </List>
      </div>
    )
  }
}

render(
  <div className='flex flex-wrap gap-3'>
    {Object.entries(sides).map(([side, drawer]) => (
      <Drawer key={side} side={side}>
        <Drawer.Trigger render={<Button>{drawer.label}, {side}</Button>} />
        <Drawer.Content>
          <Drawer.Header>
            <Drawer.Close render={<IconButton aria-label='Close' emphasis='normal'><XIcon weight='bold' className='size-5' /></IconButton>} />
            <Drawer.Title>{drawer.title}</Drawer.Title>
            {drawer.description ? <Drawer.Description>{drawer.description}</Drawer.Description> : null}
          </Drawer.Header>
          <Drawer.Body>{drawer.body}</Drawer.Body>
          {drawer.footer ? <Drawer.Footer>{drawer.footer}</Drawer.Footer> : null}
        </Drawer.Content>
      </Drawer>
    ))}
  </div>
)

Emphasis

emphasis on the root sets how much the drawer takes over the page behind it. normal dims and blurs the page, subtle tints it and leaves it readable, and subtler leaves it clear. A click outside still closes the drawer at any level. A small bottom or top drawer defaults to subtle, because it peeks over its page. Every other drawer defaults to normal.

<div className='flex flex-wrap gap-3'>
  {['normal', 'subtle', 'subtler'].map((emphasis) => (
    <Drawer key={emphasis} emphasis={emphasis}>
      <Drawer.Trigger render={<Button>{emphasis}</Button>} />
      <Drawer.Content size='sm'>
        <Drawer.Header>
          <Drawer.Title>Add ons</Drawer.Title>
          <Drawer.Description>Paperbark Sessions · Sat 14 Nov</Drawer.Description>
        </Drawer.Header>
        <Drawer.Body>
          <List emphasis='subtler'>
            <List.Item title='Parking' description='Car park B, all day' chevron={false} trailing='$25.00' />
            <List.Item title='Camping' description='Unpowered site, two nights' chevron={false} trailing='$60.00' />
          </List>
        </Drawer.Body>
        <Drawer.Footer>
          <Button intent='accent' emphasis='strong' className='w-full'>Continue</Button>
        </Drawer.Footer>
      </Drawer.Content>
    </Drawer>
  ))}
</div>

Sizes

size sets the drawer's extent along its own axis: height for a bottom or top drawer, width for a side one. sm, md and lg are fixed, so the drawer holds still while its content changes, and fit follows the content.

A bottom or top drawer measures its height against the space it can use. That space leaves out the far edge's safe area and a gap, so a strip of page always shows, like an iOS large sheet. In a wider window it also leaves out the float off the near edge.

SizeBottom or top drawerUse for
fitThe content's height, up to all of the space. The defaultShort choices that don't change while open, such as a menu, a switcher or a confirmation
smHalf the spaceA peek at something while the page stays in view. Its backdrop is subtle by default
mdThree quarters of the spaceA longer look that still keeps the page's top in view
lgAll of the spaceTasks: multi-step flows, long lists with filters and forms

A side drawer takes md by default, and fit there sizes to the content's width up to the lg width.

A bottom or top drawer runs edge to edge on a phone. In a wider window it stops at max-w-xl, centred, and floats 0.5rem off its edge with every corner rounded, the same shape as the cart drawer. Long content scrolls in Drawer.Body between a fixed header and footer.

const holders = [
  'Alex Reid', 'Sam Okafor', 'Mia Chen', 'Noah Bell', 'Priya Nair',
  'Amelia Hart', 'Omar Haddad', 'Lily Tran', 'Archie Moss', 'Lucas Ferreira',
  'Sami Kaur', 'Nora Quinn', 'Xavier Lim', 'Dani Brooks', 'Layla Stone',
  'Ethan Cole', 'Grace Wu', 'Hamish Grant', 'Isla Murphy', 'Jack Doyle',
  'Kiara Singh', 'Leo Park', 'Maya Rossi', 'Nate Evans', 'Olive Clarke'
].map((name, index) => ({
  name,
  tickets: (index % 4) + 1,
  status: index === 0 ? 'Not shared' : index % 3 === 0 ? 'Sent' : 'Accepted'
}))

function Holders({ size }) {
  return (
    <Drawer>
      <Drawer.Trigger render={<Button>{size}</Button>} />
      <Drawer.Content size={size}>
        <Drawer.Header>
          <Drawer.Close render={<IconButton aria-label='Close' emphasis='normal'><XIcon weight='bold' className='size-5' /></IconButton>} />
          <Drawer.Title>638 tickets</Drawer.Title>
          <Drawer.Description>Weekly Pass · Mon 3 to Fri 7 May</Drawer.Description>
        </Drawer.Header>
        <Drawer.Body>
          <List emphasis='subtler'>
            {holders.map((holder) => (
              <List.Item
                key={holder.name}
                title={holder.name}
                description={`${holder.tickets} ${holder.tickets === 1 ? 'ticket' : 'tickets'}`}
                chevron={false}
                leading={
                  <IconTile>
                    <UserIcon weight='bold' />
                  </IconTile>
                }
                trailing={
                  <Badge
                    size='sm'
                    intent={holder.status === 'Accepted' ? 'success' : holder.status === 'Sent' ? 'info' : undefined}
                  >
                    {holder.status}
                  </Badge>
                }
              />
            ))}
          </List>
        </Drawer.Body>
        <Drawer.Footer>
          <Button intent='accent' emphasis='strong' className='w-full'>Share tickets</Button>
        </Drawer.Footer>
      </Drawer.Content>
    </Drawer>
  )
}

render(
  <div className='flex flex-wrap gap-3'>
    <Holders size='fit' />
    <Holders size='sm' />
    <Holders size='md' />
    <Holders size='lg' />
  </div>
)

Composition

The collapsed Drawer.Content covers the common case. Compose the parts directly when you need to reach the backdrop, the viewport or the popup separately.

<Drawer side='right'>
  <Drawer.Trigger render={<Button>Filters</Button>} />
  <Drawer.Portal>
    <Drawer.Backdrop />
    <Drawer.Viewport>
      <Drawer.Popup size='sm'>
        <Drawer.Header>
          <Drawer.Close render={<IconButton aria-label='Close' emphasis='normal'><XIcon weight='bold' className='size-5' /></IconButton>} />
          <Drawer.Title>Filters</Drawer.Title>
          <Drawer.Description>638 tickets</Drawer.Description>
        </Drawer.Header>
        <Drawer.Body>
          <Field>
            <Field.Label>Status</Field.Label>
            <RadioGroup defaultValue='all'>
              <RadioGroup.Item value='all' label='All tickets' />
              <RadioGroup.Item value='not-shared' label='Not shared' />
              <RadioGroup.Item value='shared' label='Shared' />
            </RadioGroup>
          </Field>
        </Drawer.Body>
        <Drawer.Footer>
          <Button intent='accent' emphasis='strong' className='w-full'>Show tickets</Button>
        </Drawer.Footer>
      </Drawer.Popup>
    </Drawer.Viewport>
  </Drawer.Portal>
</Drawer>

Guidelines

Name every drawer

<Drawer.Header>
<Drawer.Title>Organisations</Drawer.Title>
<Drawer.Description>Switch the organisation you're working in.</Drawer.Description>
</Drawer.Header>

Do

Start with Drawer.Header and a Drawer.Title. The title names the dialog for assistive tech and tells people what they opened. Add a Drawer.Description when the title needs context.

<Drawer.Content>
<Drawer.Body>
<h2>Organisations</h2>
…
</Drawer.Body>
</Drawer.Content>

Don’t

A heading inside the body scrolls away and doesn't name the dialog. If the content already has its own heading, pass aria-label to Drawer.Content instead.

Close sits in the top-left corner

<Drawer.Header>
<Drawer.Close
render={
<IconButton aria-label='Close' emphasis='normal'>
<XIcon weight='bold' className='size-5' />
</IconButton>
}
/>
<Drawer.Title>638 tickets</Drawer.Title>
</Drawer.Header>

Do

Put a Drawer.Close inside Drawer.Header, rendered as a normal IconButton named "Close". The header sets it in the top-left corner above the title, where Close sits in a Pane and a Dialog. If the header holds only a Close, keep it outside any wrapper with a gap, such as a steps container. The wrapper's gap adds to the space under the Close.

<Drawer.Close
render={<IconButton className='absolute top-4 right-4' … />}
/>

Don’t

Don't position it yourself, move it to the other side or put it in the body. Close in the same corner everywhere is one less thing to look for.

Tasks get a Close; quick choices close themselves

Do

Give a Close to side drawers and to md and lg sheets: the tasks, forms and long lists people work in. A swipe down isn't obvious to everyone, and it isn't available with a mouse or keyboard.

Don’t

A fit sheet that closes when you pick something, such as a switcher or a menu, doesn't need a Close. Picking, swiping, Escape and a click outside all dismiss it.

The footer holds the task's action, not a second Close

<Drawer.Footer>
<Button intent='accent' emphasis='strong' className='w-full'>
Share tickets
</Button>
</Drawer.Footer>

Do

Put the action that completes the task in Drawer.Footer. It stays in reach while the body scrolls, and on a phone a full-width button is the easiest thing to hit.

<Drawer.Footer>
<Drawer.Close render={<Button>Close</Button>} />
<Button intent='accent' emphasis='strong'>Share tickets</Button>
</Drawer.Footer>

Don’t

Don't repeat Close or Cancel in the footer when the header has one. Two ways out compete with the one way forward.

Long content scrolls in the body

<Drawer.Content>
<Drawer.Header>…</Drawer.Header>
<Drawer.Body>
{rows}
</Drawer.Body>
</Drawer.Content>

Do

Drawer.Body is the scroll region, and it tells the primitive that a drag starting there is a scroll rather than a dismiss. Long content belongs inside it.

<Drawer.Body>
<div className='overflow-y-auto'>
{rows}
</div>
</Drawer.Body>

Don’t

A second scroller inside the body competes with the swipe gesture, and the sheet stops tracking the thumb.

The handle belongs on the swipe edge

<Drawer side='bottom'>
<Drawer.Content>…</Drawer.Content>
</Drawer>

Do

Drawer.Content places it for you. On a bottom sheet it reads as the grab affordance iOS users reach for. It is off by default where the swipe axis is horizontal.

<Drawer side='right'>
<Drawer.Content handle>…</Drawer.Content>
</Drawer>

Don’t

A horizontal swipe has no matching affordance in the platform, and a stray pill at the top of a side drawer reads as decoration.

Let the page show through only when it matters

<Drawer.Content size='sm'>
{/* the event stays readable behind the tickets */}
</Drawer.Content>

Do

Leave emphasis to its default. A small sheet over a page the user is still reading keeps that page legible, and a larger one takes their focus.

<Drawer.Backdrop className='bg-[oklch(0.1_0.04_250/0.3)]' />

Don’t

Restating the scrim's colour to lighten it drifts from Roadie's the moment it changes. Set emphasis on the root instead.

One side, declared on the root

<Drawer side='right'>
<Drawer.Content>…</Drawer.Content>
</Drawer>

Do

The root sets the edge and the dismiss gesture together, and every part reads it from there. No part takes a side of its own, so the two can never disagree.

const side = isMobile ? 'bottom' : 'right'
<Drawer side={side}>…</Drawer>
// not a breakpoint list:
<Drawer side={{ base: 'bottom', md: 'right' }} />

Don’t

side is a single value, not a breakpoint list. Base UI owns the gesture in JavaScript. Pick it from your own media query in application code and pass the result.

Accessibility

  • Base UI owns the focus trap, the return focus, the Escape handling and the role='dialog' wiring.
  • Drawer.Title and Drawer.Description supply the accessible name and description. A drawer without a title has no name.
  • Drawer.Handle is aria-hidden. The gesture it hints at is not the only way to dismiss, so it carries no semantics of its own.
  • Enter and exit motion is transition-based and disabled under prefers-reduced-motion: reduce.

API reference

Drawer

childrenReactNode | PayloadChildRenderFunction<unknown>

The content of the drawer.

handleDrawerHandle<unknown>

A handle to associate the drawer with a trigger. If specified, allows detached triggers to control the drawer's open state. Can be created with the Drawer.createHandle() method.

openboolean

Whether the drawer is currently open.

defaultOpenboolean

Whether the drawer is initially open. To render a controlled drawer, use the `open` prop instead.

Defaults to false.

modalboolean | "trap-focus"

Determines if the drawer enters a modal state when open. - `true`: user interaction is limited to just the drawer: focus is trapped, document page scroll is locked, and pointer interactions on outside elements are disabled. - `false`: user interaction with the rest of the document is allowed. - `'trap-focus'`: focus is trapped inside the drawer, but document page scroll is not locked and pointer interactions outside of it remain enabled.

Defaults to true.

onOpenChange((open: boolean, eventDetails: DrawerRootChangeEventDetails) => void)

Event handler called when the drawer is opened or closed.

onOpenChangeComplete((open: boolean) => void)

Event handler called after any animations complete when the drawer is opened or closed.

disablePointerDismissalboolean

Whether to prevent the drawer from closing on outside presses. For non-modal drawers, this also prevents the drawer from closing when focus moves outside of it.

Defaults to false.

actionsRefRefObject<DrawerRootActions | null>

A ref to imperative actions. - `unmount`: Manually unmounts the drawer. Call this after any externally controlled closing animation finishes. - `close`: Closes the drawer imperatively when called.

triggerIdstring | null

ID of the trigger that the drawer is associated with. This is useful in conjunction with the `open` prop to create a controlled drawer. There's no need to specify this prop when the drawer is uncontrolled (that is, when the `open` prop is not set).

defaultTriggerIdstring | null

ID of the trigger that the drawer is associated with. This is useful in conjunction with the `defaultOpen` prop to create an initially open drawer.

snapPointsDrawerSnapPoint[]

Snap points used to position the drawer. Use numbers between 0 and 1 to represent fractions of the viewport height, numbers greater than 1 as pixel values, or strings in `px`/`rem` units (for example, `'148px'` or `'30rem'`).

snapToSequentialPointsboolean

Disables velocity-based snap skipping so drag distance determines the next snap point.

Defaults to false.

snapPointDrawerSnapPoint | null

The currently active snap point. Use with `onSnapPointChange` to control the snap point.

defaultSnapPointDrawerSnapPoint | null

The initial snap point value when uncontrolled.

onSnapPointChange((snapPoint: DrawerSnapPoint | null, eventDetails: DrawerRootSnapPointChangeEventDetails) => void)

Callback fired when the snap point changes.

side"bottom" | "top" | "left" | "right"

The edge the drawer is anchored to, which also sets the swipe direction.

Defaults to 'bottom'.

emphasis"normal" | "subtle" | "subtler"

How much the drawer takes over the page behind it. A small bottom or top drawer defaults to `subtle`, because it peeks over its page; every other drawer defaults to `normal`.

Drawer.Backdrop

classNamestring | ((state: DrawerBackdropState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

Inherited from DrawerBackdropProps

forceRenderboolean

Whether the backdrop is forced to render even when nested.

Defaults to false.

Drawer.Body

No additional props. It forwards all standard HTML attributes to the underlying element.

Drawer.Close

classNamestring | ((state: DrawerCloseState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

Inherited from NativeButtonProps

nativeButtonboolean

Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`).

Defaults to true.

Drawer.Content

classNamestring | ((state: DrawerPopupState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

intent"neutral" | "brand" | "brand-secondary" | "accent" | "danger" | "success" | "warning" | "info"

The colour palette; inherited when unset.

size"fit" | "sm" | "md" | "lg"

Height for a bottom or top drawer, width for a side one. `sm`, `md` and `lg` are fixed; `fit` follows the content. Defaults to `fit` on the top or bottom, `md` on a side.

handleboolean

Renders the grab handle; on by default for bottom and top drawers.

Inherited from DrawerPopupProps

initialFocusboolean | RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null)

Determines the element to focus when the drawer is opened. - `false`: Do not move focus. - `true`: Move focus based on the default behavior (first tabbable element or popup). - `RefObject`: Move focus to the ref element. - `function`: Called with the interaction type (`mouse`, `touch`, `pen`, or `keyboard`). Return an element to focus, `true` to use the default behavior, or `false`/`undefined` to do nothing.

finalFocusboolean | RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null)

Determines the element to focus when the drawer is closed. - `false`: Do not move focus. - `true`: Move focus based on the default behavior (trigger or previously focused element). - `RefObject`: Move focus to the ref element. - `function`: Called with the interaction type (`mouse`, `touch`, `pen`, or `keyboard`). Return an element to focus, `true` to use the default behavior, or `false`/`undefined` to do nothing.

Drawer.Description

No additional props. It forwards all standard HTML attributes to the underlying element.

Drawer.Footer

No additional props. It forwards all standard HTML attributes to the underlying element.

Drawer.Handle

No additional props. It forwards all standard HTML attributes to the underlying element.

Drawer.Header

No additional props. It forwards all standard HTML attributes to the underlying element.

Drawer.Popup

classNamestring | ((state: DrawerPopupState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

intent"neutral" | "brand" | "brand-secondary" | "accent" | "danger" | "success" | "warning" | "info"

The colour palette; inherited when unset.

size"fit" | "sm" | "md" | "lg"

Height for a bottom or top drawer, width for a side one. `sm`, `md` and `lg` are fixed; `fit` follows the content. Defaults to `fit` on the top or bottom, `md` on a side.

Inherited from DrawerPopupProps

initialFocusboolean | RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null)

Determines the element to focus when the drawer is opened. - `false`: Do not move focus. - `true`: Move focus based on the default behavior (first tabbable element or popup). - `RefObject`: Move focus to the ref element. - `function`: Called with the interaction type (`mouse`, `touch`, `pen`, or `keyboard`). Return an element to focus, `true` to use the default behavior, or `false`/`undefined` to do nothing.

finalFocusboolean | RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null)

Determines the element to focus when the drawer is closed. - `false`: Do not move focus. - `true`: Move focus based on the default behavior (trigger or previously focused element). - `RefObject`: Move focus to the ref element. - `function`: Called with the interaction type (`mouse`, `touch`, `pen`, or `keyboard`). Return an element to focus, `true` to use the default behavior, or `false`/`undefined` to do nothing.

Drawer.Portal

classNamestring | ((state: DrawerPortalState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

Inherited from DrawerPortalProps

keepMountedboolean

Whether to keep the portal mounted in the DOM while the popup is hidden.

Defaults to false.

containerHTMLElement | ShadowRoot | RefObject<HTMLElement | ShadowRoot | null> | null

A parent element to render the portal element into.

Drawer.SwipeArea

An unpositioned, invisible strip that opens the drawer on a swipe.

classNamestring | ((state: DrawerSwipeAreaState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

Inherited from DrawerSwipeAreaProps

disabledboolean

Whether the swipe area is disabled.

Defaults to false.

swipeDirection"left" | "right" | "up" | "down"

The swipe direction that opens the drawer. Defaults to the opposite of `Drawer.Root` `swipeDirection`.

Drawer.Title

No additional props. It forwards all standard HTML attributes to the underlying element.

Drawer.Trigger

classNamestring | ((state: DrawerTriggerState) => string)

CSS class applied to the element, or a function that returns a class based on the component's state.

Inherited from DrawerTriggerProps

handleDrawerHandle<unknown>

A handle to associate the trigger with a drawer. Can be created with the Drawer.createHandle() method.

payloadunknown

A payload to pass to the drawer when it is opened.

idstring

ID of the trigger. In addition to being forwarded to the rendered element, it is also used to specify the active trigger for drawers in controlled mode (with the Drawer.Root `triggerId` prop).

Inherited from NativeButtonProps

nativeButtonboolean

Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`).

Defaults to true.

Drawer.Viewport

No additional props. It forwards all standard HTML attributes to the underlying element.

Previous page← DialogNext pageMenu →