Skip to content

Drawer

Terminal window
npx bambiui add drawer

Import the token file once from your global stylesheet:

@import "./styles/bambi.css";

Then import the component:

import {
DrawerRoot, DrawerTrigger, DrawerPortal, DrawerOverlay,
DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription,
DrawerBody, DrawerFooter, DrawerClose,
} from './components/ui/drawer';

Use props-driven root props for common drawers. Use the compound API for advanced layouts, custom structure, or full control. Both styles preserve the same dialog, overlay, close, header, body, and footer structure.

const [open, setOpen] = useState(false);
<DrawerRoot
open={open}
onOpenChange={setOpen}
trigger={<Button>Open Drawer</Button>}
title="Edit profile"
description="Make changes to your profile here."
footer={<Button onClick={() => setOpen(false)}>Save</Button>}
side="right"
size="md"
>
Your content goes here.
</DrawerRoot>
const [open, setOpen] = useState(false);
<DrawerRoot open={open} onOpenChange={setOpen} side="right" size="md">
<DrawerTrigger onClick={() => setOpen(true)}>Open Drawer</DrawerTrigger>
<DrawerPortal>
<DrawerOverlay />
<DrawerContent>
<DrawerClose />
<DrawerHeader>
<DrawerTitle>Edit profile</DrawerTitle>
<DrawerDescription>Make changes to your profile here.</DrawerDescription>
</DrawerHeader>
<DrawerBody>
<p>Your content goes here.</p>
</DrawerBody>
<DrawerFooter>
<Button onClick={() => setOpen(false)}>Save</Button>
</DrawerFooter>
</DrawerContent>
</DrawerPortal>
</DrawerRoot>
<DrawerRoot side="right">...</DrawerRoot>
<DrawerRoot side="left">...</DrawerRoot>
<DrawerRoot side="top">...</DrawerRoot>
<DrawerRoot side="bottom">...</DrawerRoot>
ValueWidth / Height
sm20rem
md28rem (default)
lg36rem
xl48rem
full100vw / 100dvh
ClassElement
bambi-drawer-overlayBackground dimmer
bambi-drawer-contentThe drawer panel
bambi-drawer-headerHeader section
bambi-drawer-titleTitle <h2>
bambi-drawer-descriptionDescription text
bambi-drawer-bodyScrollable content area
bambi-drawer-footerFooter action area
bambi-drawer-closeClose × button
PropTypeDefaultDescription
side'top' | 'right' | 'bottom' | 'left''right'Which edge the drawer slides from
size'sm' | 'md' | 'lg' | 'xl' | 'full''md'Panel width or height
defaultOpenbooleanfalseInitial open state (uncontrolled)
openbooleanControlled open state
closeOnOverlayClickbooleantrueClose when clicking the overlay
onOpenChange(open: boolean) => voidCalled when open state changes
triggerframework node/snippet/slotConvenience trigger content where supported
titleframework text/node propConvenience dialog title
descriptionframework text/node propConvenience dialog description
footerframework node/snippet/slotConvenience footer content

In Astro, open/close is wired via data attributes on any element in the page:

AttributeValueEffect
data-drawer-opendrawer idOpens the drawer on click
data-drawer-closedrawer idCloses the drawer on click
  • The drawer content uses role="dialog" and aria-modal="true".
  • DrawerTitle is automatically linked to the dialog via aria-labelledby.
  • DrawerDescription is linked via aria-describedby.
  • Focus is trapped inside the drawer while open (Tab / Shift+Tab cycle within the panel).
  • Pressing Escape closes the drawer.
  • Focus is restored to the trigger element when the drawer closes.
  • While open, background content has the inert attribute set, making it unreachable by keyboard and screen readers.
  • All interactive elements must be keyboard-accessible; mouse-only interactions are not required.