

<Preview name="SheetWithScrollableContentExample" />

## Overview [#overview]

The **Sheet** component presents content in a panel that slides from any edge of the viewport. It's ideal for lightweight flows such as settings, filters, or multi-step forms. Built on Base UI Dialog primitives with Framer Motion for smooth, customizable animations.

## Usage [#usage]

```tsx
import {
  Sheet,
  SheetTrigger,
  SheetContent,
  SheetHeader,
  SheetBody,
  SheetFooter,
  SheetTitle,
  SheetDescription,
  SheetClose,
} from "@tilt-legal/cubitt-components/primitives";
```

```tsx
<Sheet>
  <SheetTrigger render={<Button />}>Open Sheet</SheetTrigger>
  <SheetContent side="right">
    <SheetHeader>
      <SheetTitle>Sheet Title</SheetTitle>
      <SheetDescription>Sheet description goes here.</SheetDescription>
    </SheetHeader>
  </SheetContent>
</Sheet>
```

<Accordions type="single">
  <Accordion title="URL State Management">
    Using Cubitt's URL-state hooks, you can sync the sheet state with the URL by providing a `paramName`:

    ```tsx
    // Opens when ?settings=true in URL
    <Sheet paramName="settings">
      <SheetTrigger render={<Button />}>Open Settings</SheetTrigger>
      <SheetContent side="right">
        <SheetHeader>
          <SheetTitle>Settings</SheetTitle>
          <SheetDescription>
            Configure your preferences.
          </SheetDescription>
        </SheetHeader>
      </SheetContent>
    </Sheet>

    // Advanced options:
    <Sheet
      paramName="sheet"
      paramClearOnDefault={true}    // Remove param when closed
      onUrlValueChange={(value) => console.log('Sheet open:', value)}
    >
      {/* ... */}
    </Sheet>
    ```
  </Accordion>
</Accordions>

## Examples [#examples]

### Basic [#basic]

<Preview name="BasicSheetExample" />

### With Actions [#with-actions]

<Preview name="SheetWithActionsExample" />

### With Form [#with-form]

<Preview name="SheetWithFormExample" />

### Different Sides [#different-sides]

Sheets can slide in from any edge of the viewport.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="SheetSidesExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    // Left side
    <Sheet>
      <SheetTrigger render={<Button variant="secondary" />}>Left</SheetTrigger>
      <SheetContent side="left">
        <SheetHeader>
          <SheetTitle>Left Side</SheetTitle>
          <SheetDescription>This sheet slides in from the left.</SheetDescription>
        </SheetHeader>
      </SheetContent>
    </Sheet>

    // Right side (default)
    <Sheet>
      <SheetTrigger render={<Button variant="secondary" />}>Right</SheetTrigger>
      <SheetContent side="right">
        <SheetHeader>
          <SheetTitle>Right Side</SheetTitle>
          <SheetDescription>This sheet slides in from the right.</SheetDescription>
        </SheetHeader>
      </SheetContent>
    </Sheet>

    // Top side
    <Sheet>
      <SheetTrigger render={<Button variant="secondary" />}>Top</SheetTrigger>
      <SheetContent side="top">
        <SheetHeader>
          <SheetTitle>Top Side</SheetTitle>
          <SheetDescription>This sheet slides in from the top.</SheetDescription>
        </SheetHeader>
      </SheetContent>
    </Sheet>

    // Bottom side
    <Sheet>
      <SheetTrigger render={<Button variant="secondary" />}>Bottom</SheetTrigger>
      <SheetContent side="bottom">
        <SheetHeader>
          <SheetTitle>Bottom Side</SheetTitle>
          <SheetDescription>This sheet slides in from the bottom.</SheetDescription>
        </SheetHeader>
      </SheetContent>
    </Sheet>
    ```
  </Tab>
</Tabs>

### Floating Sheet [#floating-sheet]

<Preview name="SheetFloatingExample" />

### Scrollable Content [#scrollable-content]

Sheet with long content that scrolls. The header and footer remain sticky with a blurred background effect.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview">
    <Preview name="SheetWithScrollableContentExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    <Sheet>
      <SheetTrigger render={<Button variant="secondary" />}>
        Terms & Conditions
      </SheetTrigger>
      <SheetContent side="right">
        <SheetHeader>
          <SheetTitle>Terms and Conditions</SheetTitle>
          <SheetDescription>
            Please read and accept our terms and conditions.
          </SheetDescription>
        </SheetHeader>
        <SheetBody className="space-y-4 text-sm text-fg-2">
          {/* Long content... */}
        </SheetBody>
        <SheetFooter>
          <SheetClose render={<Button variant="secondary" />}>Decline</SheetClose>
          <Button>Accept</Button>
        </SheetFooter>
      </SheetContent>
    </Sheet>
    ```
  </Tab>
</Tabs>

### Without Close Button [#without-close-button]

<Preview name="SheetWithoutCloseButtonExample" />

### URL State [#url-state]

Sheet with URL state synchronization for shareable sheet states.

<Tabs items="['Preview', 'Code']">
  <Tab value="Preview" className="p-0">
    <Preview name="SheetURLStateExample" />
  </Tab>

  <Tab value="Code">
    ```tsx
    import {
      Button,
      Sheet,
      SheetTrigger,
      SheetContent,
      SheetHeader,
      SheetBody,
      SheetTitle,
      SheetDescription,
      SheetFooter,
      SheetClose,
    } from "@tilt-legal/cubitt-components/primitives";

    // Sheet state synced with ?settings=true
    <Sheet paramName="settings" defaultOpen={false}>
      <SheetTrigger render={<Button variant="secondary" />}>
        Open with URL state
      </SheetTrigger>
      <SheetContent side="right">
        <SheetHeader>
          <SheetTitle>Settings</SheetTitle>
          <SheetDescription>
            This sheet's state is synced with the URL. Check the URL parameter when
            you open it!
          </SheetDescription>
        </SheetHeader>
        <SheetFooter>
          <SheetClose render={<Button variant="secondary" />}>Cancel</SheetClose>
          <Button>Save</Button>
        </SheetFooter>
      </SheetContent>
    </Sheet>;
    ```
  </Tab>
</Tabs>

***

## API Reference [#api-reference]

### Sheet [#sheet]

Root component that manages sheet state.

| Prop           | Type                      | Default | Description                                 |
| -------------- | ------------------------- | ------- | ------------------------------------------- |
| `open`         | `boolean`                 | -       | Controlled open state of the sheet.         |
| `defaultOpen`  | `boolean`                 | `false` | Default open state for uncontrolled usage.  |
| `onOpenChange` | `(open: boolean) => void` | -       | Callback fired when the open state changes. |
| `modal`        | `boolean`                 | `true`  | Whether the sheet is modal (locks scroll).  |

#### URL State Props [#url-state-props]

The following props enable URL state management by syncing the sheet open/closed state with URL parameters via TanStack Router search params. When `paramName` is provided, the sheet's state will be reflected in the URL.

| Prop                  | Type                               | Default | Description                                                                                 |
| --------------------- | ---------------------------------- | ------- | ------------------------------------------------------------------------------------------- |
| `paramName`           | `string`                           | -       | URL parameter name for syncing state with URL. When provided, enables URL state management. |
| `paramMatchValue`     | `string`                           | -       | Value that must match for the sheet to open.                                                |
| `onUrlValueChange`    | `(value: boolean \| null) => void` | -       | Callback when URL parameter value changes.                                                  |
| `paramClearOnDefault` | `boolean`                          | `true`  | Remove URL param when value equals default.                                                 |
| `paramThrottle`       | `number`                           | -       | Throttle URL updates in milliseconds.                                                       |
| `paramDebounce`       | `number`                           | -       | Debounce URL updates in milliseconds.                                                       |

### SheetTrigger [#sheettrigger]

Button that opens the sheet.

| Prop     | Type                 | Default | Description                       |
| -------- | -------------------- | ------- | --------------------------------- |
| `render` | `React.ReactElement` | -       | Custom trigger element to render. |

### SheetContent [#sheetcontent]

Container for the sheet content with backdrop and animations.

| Prop              | Type                                     | Default   | Description                                                         |
| ----------------- | ---------------------------------------- | --------- | ------------------------------------------------------------------- |
| `side`            | `'top' \| 'right' \| 'bottom' \| 'left'` | `'right'` | Which edge the sheet slides from.                                   |
| `float`           | `boolean`                                | `false`   | Renders content as a floating card with rounded corners and border. |
| `showCloseButton` | `boolean`                                | `true`    | Show a built-in close button in the corner.                         |
| `className`       | `string`                                 | -         | Additional CSS classes for the sheet popup.                         |

### SheetHeader / SheetFooter [#sheetheader--sheetfooter]

Container components for sheet header and footer with sticky positioning.

| Prop        | Type     | Default | Description                            |
| ----------- | -------- | ------- | -------------------------------------- |
| `className` | `string` | -       | Additional CSS classes for the header. |

### SheetBody [#sheetbody]

Container for the main content area with consistent padding.

| Prop        | Type     | Default | Description                          |
| ----------- | -------- | ------- | ------------------------------------ |
| `className` | `string` | -       | Additional CSS classes for the body. |

### SheetTitle [#sheettitle]

The accessible title of the sheet.

| Prop        | Type     | Default | Description                           |
| ----------- | -------- | ------- | ------------------------------------- |
| `className` | `string` | -       | Additional CSS classes for the title. |

### SheetDescription [#sheetdescription]

Description text that provides context about the sheet.

| Prop        | Type     | Default | Description                                 |
| ----------- | -------- | ------- | ------------------------------------------- |
| `className` | `string` | -       | Additional CSS classes for the description. |

### SheetClose [#sheetclose]

Button that closes the sheet.

| Prop        | Type                 | Default | Description                                  |
| ----------- | -------------------- | ------- | -------------------------------------------- |
| `render`    | `React.ReactElement` | -       | Custom close button element to render.       |
| `className` | `string`             | -       | Additional CSS classes for the close button. |
