Drawer
A sliding panel that overlays the main content, typically used for navigation, forms, or additional content. Supports multiple placements (top, right, bottom, left) with smooth animations and backdrop overlay.
Import
import { Drawer, DrawerHeader, DrawerFooter, DrawerBody } from "@sparrowengg/twigs-react";Usage
const DrawerExample = () => {
const [isDrawerOpen, setIsDrawerOpen] = React.useState(false);
return (
<>
<Drawer
isOpen={isDrawerOpen}
onClose={() => setIsDrawerOpen(false)}
placement="right"
>
<DrawerHeader>
<Heading size="h4">Edit Profile</Heading>
</DrawerHeader>
<DrawerBody>
{/* Your content here */}
</DrawerBody>
<DrawerFooter>
{/* Footer content */}
</DrawerFooter>
</Drawer>
<Button onClick={() => setIsDrawerOpen(true)}>
Open Drawer
</Button>
</>
);
};Nested drawers
Render a Drawer inside another one to stack them. There is nothing to
configure — a nested drawer is detected automatically.
Drawers behind the frontmost one step back and shrink slightly, so a peek of each stays visible and the depth of the stack is readable at a glance. Drawers of different sizes still step evenly: while nested, a drawer adopts the size of the drawer in front of it, and returns to its own size when it comes back to the front.
const NestedDrawerExample = () => {
const [isSettingsOpen, setIsSettingsOpen] = React.useState(false);
const [isBillingOpen, setIsBillingOpen] = React.useState(false);
return (
<>
<Drawer isOpen={isSettingsOpen} onClose={() => setIsSettingsOpen(false)}>
<DrawerHeader>
<Heading size="h4">Settings</Heading>
</DrawerHeader>
<DrawerBody>
<Button onClick={() => setIsBillingOpen(true)}>Open billing</Button>
{/* The nested drawer lives inside its parent, which stays mounted. */}
<Drawer isOpen={isBillingOpen} onClose={() => setIsBillingOpen(false)}>
<DrawerHeader>
<Heading size="h4">Billing</Heading>
</DrawerHeader>
<DrawerBody>{/* Your content here */}</DrawerBody>
</Drawer>
</DrawerBody>
</Drawer>
<Button onClick={() => setIsSettingsOpen(true)}>Open Drawer</Button>
</>
);
};The parent must stay mounted for the stack to hold, so keep the nested
Drawer rendered inside it rather than as a sibling.
Within a stack:
- One backdrop is shared by the whole stack, so the page behind does not get progressively darker as drawers open.
- Clicking the backdrop or pressing Escape closes only the frontmost drawer.
- Page scroll stays locked until every drawer in the stack has closed.
Styling a stacked drawer
A drawer knows where it sits in the stack and exposes that, so you can style
the ones behind through the css prop:
| Hook | Description |
|---|---|
data-nested-drawer-open | Present while at least one drawer is stacked on top of this one. Absent on the frontmost drawer. |
--nested-drawers | How many drawers are stacked on top of this one. 0 for the frontmost. |
Because the attribute comes and goes as drawers open and close, styles applied through it appear while the drawer is behind another and are dropped the moment it returns to the front — no bookkeeping of your own.
<Drawer
isOpen={isOpen}
onClose={onClose}
css={{
'&[data-nested-drawer-open]': {
opacity: 0.6
}
}}
>
<DrawerBody>{/* Your content here */}</DrawerBody>
</Drawer>Rounding the stacked drawers makes each visible edge read as its own card.
Round only the edge facing into the screen — the opposite edge sits flush
against the viewport, so rounding it would show a sliver of backdrop through
the corners. Which edge that is depends on placement; for the default
right placement it is the left one:
<Drawer
isOpen={isOpen}
onClose={onClose}
css={{
'&[data-nested-drawer-open]': {
borderTopLeftRadius: '$2xl',
borderBottomLeftRadius: '$2xl'
}
}}
>
<DrawerBody>{/* Your content here */}</DrawerBody>
</Drawer>To scale a style with how deep the stack is, use --nested-drawers in a
calc() rather than writing a rule per level:
css={{
'&[data-nested-drawer-open]': {
opacity: 'calc(1 - var(--nested-drawers) * 0.15)'
}
}}Props
Drawer
You can alter the placement of the Modal by changing the prop to top ,right ,left and bottom
| Prop | Type | Default |
|---|---|---|
isOpen? | boolean | false |
onClose? | () => void | - |
placement? | 'top' | 'left' | 'right' | 'bottom' | 'right' |
finalFocusRef? | React.RefObject<HTMLButtonElement> | - |
portalContainer? | HTMLElement | - |
children? | ReactNode | - |
css? | object | - |
DialogsManager
A comprehensive dialog management system that provides programmatic access to open, close, and manage multiple dialogs.
Dropdown
A dropdown menu that displays a list of actions or functions triggered by a button. Supports items, checkboxes, radio groups, submenus, and separators with full keyboard navigation and accessibility features.