Components Overlays
Sheet
StableA bottom sheet for mobile-first flows, with a drag handle to dismiss.
Sheet rises from the bottom of the screen and can be dragged down to close, matching how people expect overlays to behave on phones. On tablets and desktops it centres at up to 640px wide. It is a modal dialog with the same focus and Escape handling as Dialog; dragging is an addition, never the only way to close.
Overview
import { Button, Icon, Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from '@bindshq/stance-react';
export function Basic() {
return (
<Sheet>
<SheetTrigger asChild>
<Button>Share signature</Button>
</SheetTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>Share signature</SheetTitle>
<SheetDescription>
Anyone with the link can copy this signature into their mail client.
</SheetDescription>
</SheetHeader>
<SheetBody>
<code style={{ wordBreak: 'break-all' }}>https://esignify.app/s/7f3k2-sales-emea</code>
</SheetBody>
<SheetFooter>
<SheetClose asChild>
<Button variant="outline">Close</Button>
</SheetClose>
<Button leadingIcon={<Icon name="copy" />}>Copy link</Button>
</SheetFooter>
</SheetContent>
</Sheet>
);
}Light, dark and high contrast
Every Stance component is designed and tested in all three appearance modes.
Import
import { Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody, SheetFooter, SheetClose } from '@bindshq/stance-react';Platforms
Sheet has native counterparts. They share Stance tokens and intent, and use each platform’s own controls and conventions. The live previews on this page are the web implementation.
import { Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody, SheetFooter, SheetClose } from '@bindshq/stance-react';Examples
Heights
auto fits content (up to 90%), half and full are fixed.
import { Button, Sheet, SheetBody, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@bindshq/stance-react';
export function Heights() {
return (
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 12 }}>
{(['auto', 'half', 'full'] as const).map((height) => (
<Sheet key={height}>
<SheetTrigger asChild>
<Button variant="outline">{height}</Button>
</SheetTrigger>
<SheetContent height={height}>
<SheetHeader>
<SheetTitle>Height “{height}”</SheetTitle>
</SheetHeader>
<SheetBody>Drag the handle down, press Escape, or tap outside to close.</SheetBody>
</SheetContent>
</Sheet>
))}
</div>
);
}Action list
A common mobile pattern for share or more-actions menus.
import { Button, Icon, Sheet, SheetBody, SheetClose, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@bindshq/stance-react';
export function ActionList() {
const actions = [
{ icon: 'pencil', label: 'Edit template' },
{ icon: 'copy', label: 'Duplicate' },
{ icon: 'download', label: 'Download HTML' },
{ icon: 'trash', label: 'Delete' },
] as const;
return (
<Sheet>
<SheetTrigger asChild>
<Button variant="secondary" leadingIcon={<Icon name="more-horizontal" />}>
More actions
</Button>
</SheetTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>Sales, EMEA</SheetTitle>
</SheetHeader>
<SheetBody>
<div style={{ display: 'grid', gap: 4 }}>
{actions.map((a) => (
<SheetClose key={a.label} asChild>
<Button
variant="ghost"
leadingIcon={<Icon name={a.icon} />}
style={{ justifyContent: 'flex-start' }}
>
{a.label}
</Button>
</SheetClose>
))}
</div>
</SheetBody>
</SheetContent>
</Sheet>
);
}API reference
Sheet
| Prop | Type | Default | Description |
|---|---|---|---|
open / defaultOpen / onOpenChange | boolean / boolean / (open: boolean) => void | None | Same as Dialog. |
SheetTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Render your own Button as the trigger. |
SheetContent
| Prop | Type | Default | Description |
|---|---|---|---|
height | 'auto' | 'half' | 'full' | 'auto' | Sheet height. |
showHandle | boolean | true | Show the drag handle. |
dismissThreshold | number | 0.3 | Drag distance that dismisses: px, or a fraction of the height when below 1. A quick flick also dismisses. |
showClose / closeLabel | boolean / string | true / 'Close' | Close button; keep it so keyboard and switch users can close. |
SheetTitle
| Prop | Type | Default | Description |
|---|---|---|---|
level | 1 | 2 | 3 | 4 | 2 | Heading level; the accessible name. |
SheetHeader / SheetBody / SheetFooter / SheetDescription / SheetClose
| Prop | Type | Default | Description |
|---|---|---|---|
...props | HTMLAttributes | None | Layout parts shared with Dialog. |
Accessibility
- A modal dialog named by SheetTitle; the drag handle is decorative (aria-hidden).
- Dragging is an enhancement. Escape, the close button and the backdrop all close the sheet too (WCAG 2.5.7 Dragging Movements).
- Focus is trapped while open and restored on close.
Keyboard
| Keys | Action |
|---|---|
| Tab Shift+Tab | Cycle focus inside the sheet. |
| Esc | Closes the sheet. |
Usage guidelines
Do
- Use for short, mobile-first tasks: sharing, quick actions, pickers.
- Keep the close button visible.
Don’t
- Put long forms in a sheet; use a page or Drawer.