# Drawer
> A panel which slides in from the edge of the screen. Useful to focus the user's attention on a sub-task or sub-action of the parent view.

## Import

```js
import { Drawer, DrawerProvider } from '@gemini-suite/vera-react';
```

Drawer is a compound component that consists of multiple parts:

- `DrawerProvider`: The wrapper that handles the stacking behavior of drawers, by tracking their mount/unmount.
- `Drawer.Root`: The wrapper that contains all the parts of a drawer and provides context for its children.
- `Drawer.Trigger`: The button that opens the drawer.
- `Drawer.Portal`: The portal attached to the end of `document.body` into which your overlay and content parts will render.
- `Drawer.Overlay`: The dimmed overlay behind the drawer.
- `Drawer.Content`: The container for the drawer's content.
- `Drawer.Header`: The header of the the drawer.
- `Drawer.Title`: The title that labels the drawer.
- `Drawer.Body`: The wrapper that houses the drawers's main content.
- `Drawer.Close`: The button that closes the drawer.

## Examples

### Basic

`Drawer` works in an uncontrolled way by default, meaning each `Drawer` component instance is responsible for managing its own state internally.

<Callout variant="tip">

The `Drawer.Trigger` component can be instructed via `as` prop to render _as_ something else. In our case we want to render it as a `<Button>` component.

</Callout>

```jsx
<Drawer.Root>
  <Drawer.Trigger as={Button} variant="outline">
    {'Open Drawer'}
  </Drawer.Trigger>
  <Drawer.Portal>
    <Drawer.Content css={{ padding: '$spacingL' }}>
      {'Drawer content'}
      <Box marginTop="spacingM">
        <Drawer.Close as={Button} variant="outline">
          {'Close'}
        </Drawer.Close>
      </Box>
    </Drawer.Content>
  </Drawer.Portal>
</Drawer.Root>
```

### Controlled Drawer

You can easily make the drawer controlled, by passing your own state to `isOpen` prop.
`onIsOpenChange` handler is called when the state of the drawer changes, allowing you to sync state.

```jsx
() => {
  const [state, setState] = React.useState(false);

  return (
    <Drawer.Root isOpen={state} onIsOpenChange={setState}>
      <Drawer.Trigger as={Button} variant="outline">
        {'Open Drawer'}
      </Drawer.Trigger>
      <Drawer.Portal>
        <Drawer.Content css={{ padding: '$spacingL' }}>
          {'Drawer content'}
          <Box marginTop="spacingM">
            <Drawer.Close as={Button} variant="outline">
              {'Close'}
            </Drawer.Close>
          </Box>
        </Drawer.Content>
      </Drawer.Portal>
    </Drawer.Root>
  );
};
```

### Drawer with header and body

Use `Drawer.Header`, `Drawer.Title`, `Drawer.Close` and `Drawer.Body` parts to construct drawer with a header and scrollable content.

- `Drawer.Title` takes the title you pass in and labels the drawer with it through ARIA attributes to provide accessible experience.
- `Drawer.Body` ensures that the body of the drawer becomes scrollable when its content overflows the available height.

```jsx
<Drawer.Root>
  <Drawer.Trigger as={Button} variant="outline">
    {'Open Drawer'}
  </Drawer.Trigger>
  <Drawer.Portal>
    <Drawer.Content>
      <Drawer.Header>
        <Drawer.Title>{'Drawer title'}</Drawer.Title>
        <Drawer.Close
          as={Button}
          variant="ghost"
          shape="circle"
          withLoneIcon
          aria-label="Close"
          marginLeft="auto"
        >
          <SvgIcon iconName="close" />
        </Drawer.Close>
      </Drawer.Header>
      <Drawer.Body>
        <p>
          {
            'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quas perspiciatis provident sit ut dolorem velit obcaecati, expedita minus cumque commodi repudiandae temporibus veniam non accusamus hic tenetur optio sapiente id?'
          }
        </p>
        <p>
          {
            'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quas perspiciatis provident sit ut dolorem velit obcaecati, expedita minus cumque commodi repudiandae temporibus veniam non accusamus hic tenetur optio sapiente id?'
          }
        </p>
      </Drawer.Body>
    </Drawer.Content>
  </Drawer.Portal>
</Drawer.Root>
```

### Drawer with overlay

Use `Drawer.Overlay` part to cover the inert part of the view when the drawer is open.

```jsx
<Drawer.Root>
  <Drawer.Trigger as={Button} variant="outline">
    {'Open Drawer'}
  </Drawer.Trigger>
  <Drawer.Portal>
    <Drawer.Overlay />
    <Drawer.Content>
      <Drawer.Header>
        <Drawer.Title>{'Drawer title'}</Drawer.Title>
        <Drawer.Close
          as={Button}
          variant="ghost"
          shape="circle"
          withLoneIcon
          aria-label="Close"
          marginLeft="auto"
        >
          <SvgIcon iconName="close" />
        </Drawer.Close>
      </Drawer.Header>
      <Drawer.Body>
        <p>
          {
            'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quas perspiciatis provident sit ut dolorem velit obcaecati, expedita minus cumque commodi repudiandae temporibus veniam non accusamus hic tenetur optio sapiente id?'
          }
        </p>
      </Drawer.Body>
    </Drawer.Content>
  </Drawer.Portal>
</Drawer.Root>
```

### Customizable width

Use `maxWidth` prop to set the maximum width of the `Drawer.Content`. If the viewport width is less than that, the drawer will shrink accordingly.

```jsx
<Drawer.Root>
  <Drawer.Trigger as={Button} variant="outline">
    {'Open Drawer'}
  </Drawer.Trigger>
  <Drawer.Portal>
    <Drawer.Content maxWidth="50rem" css={{ padding: '$spacingL' }}>
      {'Drawer content'}
      <Box marginTop="spacingM">
        <Drawer.Close as={Button} variant="outline">
          {'Close'}
        </Drawer.Close>
      </Box>
    </Drawer.Content>
  </Drawer.Portal>
</Drawer.Root>
```

### Sizes

Use `size` prop to control the size of the `Drawer`. `medium` and `small` sizes are available, with the `medium` size used by default.

```jsx
<Flex flow="column" cross="start">
  <Drawer.Root size="small">
    <Drawer.Trigger as={Button} variant="outline" size="small">
      {'Open small drawer'}
    </Drawer.Trigger>
    <Drawer.Portal>
      <Drawer.Content maxWidth="26rem">
        <Drawer.Header>
          <Drawer.Title>{'My small drawer'}</Drawer.Title>
          <Drawer.Close
            as={Button}
            variant="ghost"
            shape="circle"
            withLoneIcon
            aria-label="Close"
            marginLeft="auto"
          >
            <SvgIcon iconName="close" />
          </Drawer.Close>
        </Drawer.Header>
        <Drawer.Body>
          <Flex flow="column" gap="spacingS">
            <Text>
              {
                'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quas perspiciatis provident sit ut dolorem velit obcaecati, expedita minus cumque commodi repudiandae temporibus veniam non accusamus hic tenetur optio sapiente id?'
              }
            </Text>
            <Text>
              {
                'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quas perspiciatis provident sit ut dolorem velit obcaecati, expedita minus cumque commodi repudiandae temporibus veniam non accusamus hic tenetur optio sapiente id?'
              }
            </Text>
          </Flex>
        </Drawer.Body>
      </Drawer.Content>
    </Drawer.Portal>
  </Drawer.Root>
  <Drawer.Root>
    <Drawer.Trigger as={Button} variant="outline">
      {'Open medium drawer'}
    </Drawer.Trigger>
    <Drawer.Portal>
      <Drawer.Content>
        <Drawer.Header>
          <Drawer.Title>{'My medium drawer'}</Drawer.Title>
          <Drawer.Close
            as={Button}
            variant="ghost"
            shape="circle"
            withLoneIcon
            aria-label="Close"
            marginLeft="auto"
          >
            <SvgIcon iconName="close" />
          </Drawer.Close>
        </Drawer.Header>
        <Drawer.Body>
          <Flex flow="column">
            <Text>
              {
                'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quas perspiciatis provident sit ut dolorem velit obcaecati, expedita minus cumque commodi repudiandae temporibus veniam non accusamus hic tenetur optio sapiente id?'
              }
            </Text>
            <Text>
              {
                'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quas perspiciatis provident sit ut dolorem velit obcaecati, expedita minus cumque commodi repudiandae temporibus veniam non accusamus hic tenetur optio sapiente id?'
              }
            </Text>
          </Flex>
        </Drawer.Body>
      </Drawer.Content>
    </Drawer.Portal>
  </Drawer.Root>
</Flex>
```

### Drawer placement

Use `side` prop to anchor `Drawer.Content` to the left or right edge of the screen.

```jsx
<Flex>
  <Drawer.Root>
    <Drawer.Trigger as={Button} variant="outline">
      {'Open Left Drawer'}
    </Drawer.Trigger>
    <Drawer.Portal>
      <Drawer.Content side="left" css={{ padding: '$spacingL' }}>
        {'Left drawer content'}
        <Box marginTop="spacingM">
          <Drawer.Close as={Button} variant="outline">
            {'Close'}
          </Drawer.Close>
        </Box>
      </Drawer.Content>
    </Drawer.Portal>
  </Drawer.Root>
  <Drawer.Root>
    <Drawer.Trigger as={Button} variant="outline">
      {'Open Right Drawer'}
    </Drawer.Trigger>
    <Drawer.Portal>
      <Drawer.Content side="right" css={{ padding: '$spacingL' }}>
        {'Right drawer content'}
        <Box marginTop="spacingM">
          <Drawer.Close as={Button} variant="outline">
            {'Close'}
          </Drawer.Close>
        </Box>
      </Drawer.Content>
    </Drawer.Portal>
  </Drawer.Root>
</Flex>
```

### Non-modal drawer

You can configure the drawer to be non-modal, which allows the users to interact with the rest of the view.

```jsx
<Drawer.Root>
  <Drawer.Trigger as={Button} variant="outline">
    {'Open Drawer'}
  </Drawer.Trigger>
  <Drawer.Portal shouldCloseOnOutsideClick={false}>
    <Drawer.Content
      autoFocusFirst={false}
      containFocus={false}
      css={{ padding: '$spacingL' }}
    >
      {'Non-modal drawer content'}
      <Box marginTop="spacingM">
        <Drawer.Close as={Button} variant="outline">
          {'Close'}
        </Drawer.Close>
      </Box>
    </Drawer.Content>
  </Drawer.Portal>
</Drawer.Root>
```

### Stacking

To handle the stacking behaviour of drawers, you must wrap your drawers with the `<DrawerProvider/>`.

```jsx
<DrawerProvider>
  <Drawer.Root>
    <Drawer.Trigger as={Button} variant="outline">
      {'Open Drawer'}
    </Drawer.Trigger>
    <Drawer.Portal>
      <Drawer.Overlay />
      <Drawer.Content css={{ padding: '$spacingL' }}>
        {'Drawer content'}
        <Flex marginTop="spacingM">
          <Drawer.Root>
            <Drawer.Trigger as={Button} variant="outline">
              {'Open Nested Drawer'}
            </Drawer.Trigger>
            <Drawer.Portal>
              <Drawer.Overlay />
              <Drawer.Content css={{ padding: '$spacingL' }}>
                {'Inner drawer content'}
                <Box marginTop="spacingM">
                  <Drawer.Close as={Button} variant="outline">
                    {'Close'}
                  </Drawer.Close>
                </Box>
              </Drawer.Content>
            </Drawer.Portal>
          </Drawer.Root>
          <Drawer.Close as={Button} variant="outline">
            {'Close'}
          </Drawer.Close>
        </Flex>
      </Drawer.Content>
    </Drawer.Portal>
  </Drawer.Root>
</DrawerProvider>
```

---

## Accessibility features

<ul>
  <li iconName="check">

When the drawer opens, focus is sent into the drawer and set to the first tabbable element. Focus stays trapped within the drawer until close is requested. After closing, focus is restored back to the trigger element.

  </li>
  <li iconName="check">

Clicking on the overlay or outside the content closes the drawer.

  </li>
  <li iconName="check">

Pressing ESC closes the drawer.

  </li>
  <li iconName="check">

Scrolling is blocked on the elements behind the drawer with overlay.

  </li>
  <li iconName="check">

The drawer is portalled (via [Portal utility](/components/utility/portal)) to the end of `document.body` to break it out of the source order.

  </li>
  <li iconName="check">

Manages screen reader announcements with `Drawer.Title`.

  </li>
</ul>

---

## API Reference

### Drawer

| Name             | Type                        | Default    | Description                                                                                            | Required |
| ---------------- | --------------------------- | ---------- | ------------------------------------------------------------------------------------------------------ | -------- |
| `isOpen`         | `boolean`                   | `false`    | The controlled open state of the drawer. Use in conjunction with `onIsOpenChange`.                     |          |
| `defaultIsOpen`  | `boolean`                   |            | The open state of the drawer when it's first rendered. Use when you do not need to control open state. |          |
| `onIsOpenChange` | `(isOpen: boolean) => void` |            | Event handler called when the open state of the drawer changes.                                        |          |
| `size`           | `"small" \| "medium"`       | `"medium"` | Size of the drawer.                                                                                    |          |

### Drawer.Trigger

| Name  | Type                                                      | Default  | Description                                                                                                                                                                                                                                                                                  | Required |
| ----- | --------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`  | `keyof JSX.IntrinsicElements \| React.ComponentType<any>` | `button` | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](/get-started/composition#polymorphism) guide. |          |
| `css` | `StitchesCss`                                             |          | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants.                                                |          |

### Drawer.Portal

| Name                        | Type         | Default      | Description                                                                           | Required |
| --------------------------- | ------------ | ------------ | ------------------------------------------------------------------------------------- | -------- |
| `onExitComplete`            | `() => void` | `() => void` | Event handler called when the drawer has completed animating out.                     |          |
| `shouldCloseOnOutsideClick` | `boolean`    | `true`       | When `true`, the drawer portal will close when clicking outside the drawer's content. |          |
| `shouldCloseOnEsc`          | `boolean`    | `true`       | When `true`, the drawer portal will close when the `Esc` key is pressed.              |          |

### Drawer.Overlay

| Name                 | Type      | Default | Description                                       | Required |
| -------------------- | --------- | ------- | ------------------------------------------------- | -------- |
| `shouldCloseOnClick` | `boolean` | `true`  | When `true`, the overlay will close when clicked. |          |

### Drawer.Content

| Name             | Type                | Default   | Description                                                                                                                                                                                                                                   | Required |
| ---------------- | ------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`            | `StitchesCss`       |           | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants. |          |
| `maxWidth`       | `string \| number`  | `"32rem"` | Maximum width of the drawer's content. When larger than viewport, the content will shrink accordingly.                                                                                                                                        |          |
| `side`           | `"left" \| "right"` | `"right"` | The side on which the drawer will be positioned.                                                                                                                                                                                              |          |
| `autoFocusFirst` | `boolean`           | `true`    | When `true`, the first focusable element of the drawer's content will be automatically focused.                                                                                                                                               |          |
| `containFocus`   | `boolean`           | `true`    | When `true`, the focus will be contained inside the drawer. You can set it to `false` in situations where you want the user to be able to interact with other surrounding elements, though this is not recommended for accessibility reasons. |          |

### Drawer.Header

<Callout variant="tip">

In addition to the props below, you can pass [Box props](/components/layout/box#api-reference) to control the spacing.

</Callout>

| Name  | Type          | Default | Description                                                                                                                                                                                                                                   | Required |
| ----- | ------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css` | `StitchesCss` |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants. |          |

### Drawer.Title

<Callout variant="tip">

In addition to the props below, you can pass [Heading props](/components/typography/heading#api-reference).

</Callout>

| Name  | Type                                                      | Default   | Description                                                                                                                                                                                                                                                                                  | Required |
| ----- | --------------------------------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`  | `keyof JSX.IntrinsicElements \| React.ComponentType<any>` | `heading` | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](/get-started/composition#polymorphism) guide. |          |
| `css` | `StitchesCss`                                             |           | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants.                                                |          |

### Drawer.Body

<Callout variant="tip">

In addition to the props below, you can pass [Box props](/components/layout/box#api-reference) to control the spacing.

</Callout>

| Name  | Type          | Default | Description                                                                                                                                                                                                                                   | Required |
| ----- | ------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css` | `StitchesCss` |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants. |          |

### Drawer.Close

| Name  | Type                                                      | Default  | Description                                                                                                                                                                                                                                                                                  | Required |
| ----- | --------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`  | `keyof JSX.IntrinsicElements \| React.ComponentType<any>` | `button` | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](/get-started/composition#polymorphism) guide. |          |
| `css` | `StitchesCss`                                             |          | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants.                                                |          |
