# Sheet

> A bottom sheet for mobile-first flows, with a drag handle to dismiss.

Category: Overlays · Status: stable · Package: @bindshq/stance-react@0.1.1 · Docs: https://stanceui.com/components/sheet

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.

## Import

```tsx
import { Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody, SheetFooter, SheetClose } from '@bindshq/stance-react';
```

Styles are imported once per app: `@bindshq/stance-tokens/css` and `@bindshq/stance-react/styles.css`.

## Examples

### Basic

```tsx
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>
  );
}
```

### Heights

auto fits content (up to 90%), half and full are fixed.

```tsx
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.

```tsx
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

| Component | Prop | Type | Default | Description |
| --- | --- | --- | --- | --- |
| Sheet | `open / defaultOpen / onOpenChange` | `boolean / boolean / (open: boolean) => void` | None | Same as Dialog. |
| SheetTrigger | `asChild` | `boolean` | `false` | Render your own Button as the trigger. |
| SheetContent | `height` | `'auto' \| 'half' \| 'full'` | `'auto'` | Sheet height. |
| SheetContent | `showHandle` | `boolean` | `true` | Show the drag handle. |
| SheetContent | `dismissThreshold` | `number` | `0.3` | Drag distance that dismisses: px, or a fraction of the height when below 1. A quick flick also dismisses. |
| SheetContent | `showClose / closeLabel` | `boolean / string` | `true / 'Close'` | Close button; keep it so keyboard and switch users can close. |
| SheetTitle | `level` | `1 \| 2 \| 3 \| 4` | `2` | Heading level; the accessible name. |
| SheetHeader / SheetBody / SheetFooter / SheetDescription / SheetClose | `...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. |

## 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.

## Related

- https://stanceui.com/components/drawer
- https://stanceui.com/components/dialog
- https://stanceui.com/components/action-menu
