> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.

# Drawer

A side panel for work the reader does alongside the page — its differentiator is that it can run non-modal, so with `modal={false}` they can keep using what is behind it. Reach for `Dialog` when the panel is a decision to resolve now, since that is the same panel with the exit closed off.

[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/Drawer) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/overlay-drawer--overview)

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';

export const Basic = () => {
  return (
    <Drawer data-testid='drawer'>
      <DrawerTrigger asChild>
        <Button variant='outline' color='neutral'>
          Open Drawer
        </Button>
      </DrawerTrigger>

      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>Drawer Title</DrawerTitle>
        </DrawerHeader>

        <DrawerBody>
          <ContentPlaceholder fillHeight />
        </DrawerBody>
      </DrawerContent>
    </Drawer>
  );
};
```

## Usage

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';
```

## Examples

### With footer

`DrawerFooter` for the actions that commit the work. It stays put while the body scrolls.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerContent, DrawerFooter, DrawerFooterControls, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';

export const WithFooter = () => {
  return (
    <Drawer>
      <DrawerTrigger asChild>
        <Button variant='outline' color='neutral'>
          Open Drawer
        </Button>
      </DrawerTrigger>

      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>Drawer's title</DrawerTitle>
        </DrawerHeader>
        <DrawerBody>
          <ContentPlaceholder fillHeight />
        </DrawerBody>
        <DrawerFooter>
          <DrawerFooterControls>
            <Button variant='ghost' color='neutral' size='large'>
              Button
            </Button>
            <Button variant='primary' color='brand' size='large'>
              Button
            </Button>
          </DrawerFooterControls>
        </DrawerFooter>
      </DrawerContent>
    </Drawer>
  );
};
```

### With description

`DrawerDescription` under the title, wired to `aria-describedby` so it is read with the panel
rather than after it. `DrawerHeader` stacks the two, keeping the close button right-aligned.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerContent, DrawerDescription, DrawerFooter, DrawerFooterControls, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';

export const WithDescription = () => {
  return (
    <Drawer>
      <DrawerTrigger asChild>
        <Button variant='outline' color='neutral'>
          Open Drawer
        </Button>
      </DrawerTrigger>

      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>Drawer's title</DrawerTitle>
          <DrawerDescription>Description TBD</DrawerDescription>
        </DrawerHeader>
        <DrawerBody>
          <ContentPlaceholder fillHeight />
        </DrawerBody>
        <DrawerFooter>
          <DrawerFooterControls>
            <Button variant='ghost' color='neutral' size='large'>
              Button
            </Button>
            <Button variant='primary' color='brand' size='large'>
              Button
            </Button>
          </DrawerFooterControls>
        </DrawerFooter>
      </DrawerContent>
    </Drawer>
  );
};
```

### With footer left actions

A left-aligned secondary action, separated from the committing pair on the right.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerFooter, DrawerFooterControls, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';
import { Switch, SwitchControl, SwitchLabel } from '@wallarm-org/design-system/Switch';

export const WithFooterLeftActions = () => (
  <Drawer>
    <DrawerTrigger asChild>
      <Button variant='outline' color='neutral'>
        Open with Footer Actions
      </Button>
    </DrawerTrigger>
    <DrawerContent>
      <DrawerHeader>
        <DrawerTitle>Footer with Left Actions</DrawerTitle>
      </DrawerHeader>

      <DrawerBody>
        <ContentPlaceholder fillHeight />
      </DrawerBody>

      <DrawerFooter>
        <DrawerFooterControls placement='left'>
          <Switch>
            <SwitchControl />
            <SwitchLabel>Remember choice</SwitchLabel>
          </Switch>
        </DrawerFooterControls>

        <DrawerClose asChild>
          <Button variant='ghost' color='neutral' size='large'>
            Cancel
          </Button>
        </DrawerClose>
        <Button variant='primary' color='brand' size='large'>
          Apply
        </Button>
      </DrawerFooter>
    </DrawerContent>
  </Drawer>
);
```

### Sizes

The standard widths. A drawer earns more width than a dialog because the reader is working in
it rather than answering it.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';
import { HStack } from '@wallarm-org/design-system/Stack';
import { DRAWER_SIZES } from './constants';

export const Sizes = () => (
  <HStack gap={8} justify='center'>
    <Drawer width={DRAWER_SIZES.small}>
      <DrawerTrigger asChild>
        <Button variant='outline' color='neutral'>
          Open Small ({DRAWER_SIZES.small}px)
        </Button>
      </DrawerTrigger>
      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>Small Drawer ({DRAWER_SIZES.small}px)</DrawerTitle>
        </DrawerHeader>

        <DrawerBody>
          <ContentPlaceholder fillHeight />
        </DrawerBody>
      </DrawerContent>
    </Drawer>

    <Drawer width={DRAWER_SIZES.medium}>
      <DrawerTrigger asChild>
        <Button variant='outline' color='neutral'>
          Open Medium ({DRAWER_SIZES.medium}px)
        </Button>
      </DrawerTrigger>
      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>Medium Drawer ({DRAWER_SIZES.medium}px)</DrawerTitle>
        </DrawerHeader>

        <DrawerBody>
          <ContentPlaceholder fillHeight />
        </DrawerBody>
      </DrawerContent>
    </Drawer>

    <Drawer width={DRAWER_SIZES.large}>
      <DrawerTrigger asChild>
        <Button variant='outline' color='neutral'>
          Open Large ({DRAWER_SIZES.large}px)
        </Button>
      </DrawerTrigger>
      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>Large Drawer ({DRAWER_SIZES.large}px)</DrawerTitle>
        </DrawerHeader>

        <DrawerBody>
          <ContentPlaceholder fillHeight />
        </DrawerBody>
      </DrawerContent>
    </Drawer>
  </HStack>
);
```

### Custom sizes

Explicit width bounds for content the standard steps do not fit.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';
import { HStack } from '@wallarm-org/design-system/Stack';

export const CustomSizes = () => {
  return (
    <HStack gap={8} justify='center'>
      <Drawer width='50%'>
        <DrawerTrigger asChild>
          <Button variant='outline' color='neutral'>
            50% Width
          </Button>
        </DrawerTrigger>

        <DrawerContent>
          <DrawerHeader>
            <DrawerTitle>50% Width Drawer</DrawerTitle>
          </DrawerHeader>

          <DrawerBody>
            <div className='py-12'>
              <p>This drawer takes 50% of the viewport width.</p>
            </div>
          </DrawerBody>
        </DrawerContent>
      </Drawer>

      <Drawer width={1000}>
        <DrawerTrigger asChild>
          <Button variant='outline' color='neutral'>
            1000px Width
          </Button>
        </DrawerTrigger>

        <DrawerContent>
          <DrawerHeader>
            <DrawerTitle>1000px Width Drawer</DrawerTitle>
          </DrawerHeader>

          <DrawerBody>
            <div className='py-12'>
              <p>This drawer has a fixed width of 1000px.</p>
            </div>
          </DrawerBody>
        </DrawerContent>
      </Drawer>
    </HStack>
  );
};
```

### Resizable

`DrawerResizeHandle` lets the reader set the width themselves — the part `Dialog`
deliberately does not export, because a decision does not need resizing.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerResizeHandle, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';
import { VStack } from '@wallarm-org/design-system/Stack';

export const Resizable = () => {
  return (
    <VStack gap={12}>
      <Drawer width={800}>
        <DrawerTrigger asChild>
          <Button variant='outline' color='neutral'>
            Open Resizable Drawer (as number)
          </Button>
        </DrawerTrigger>

        <DrawerContent>
          <DrawerResizeHandle />
          <DrawerHeader>
            <DrawerTitle>Resizable Drawer</DrawerTitle>
          </DrawerHeader>

          <DrawerBody>
            <div className='py-12'>
              <p className='mb-16'>Drag the left edge to resize this drawer.</p>
              <ContentPlaceholder height={300} />
            </div>
          </DrawerBody>
        </DrawerContent>
      </Drawer>

      <Drawer width='900px'>
        <DrawerTrigger asChild>
          <Button variant='outline' color='neutral'>
            Open Resizable Drawer (900px)
          </Button>
        </DrawerTrigger>

        <DrawerContent>
          <DrawerResizeHandle />
          <DrawerHeader>
            <DrawerTitle>Resizable Drawer with "900px" width</DrawerTitle>
          </DrawerHeader>

          <DrawerBody>
            <div className='py-12'>
              <p className='mb-16'>Width is set as "900px" string. Drag the left edge to resize.</p>
              <ContentPlaceholder height={300} />
            </div>
          </DrawerBody>
        </DrawerContent>
      </Drawer>

      <Drawer width='50%'>
        <DrawerTrigger asChild>
          <Button variant='outline' color='neutral'>
            Open Resizable Drawer (50%)
          </Button>
        </DrawerTrigger>

        <DrawerContent>
          <DrawerResizeHandle />
          <DrawerHeader>
            <DrawerTitle>Resizable Drawer with 50% width</DrawerTitle>
          </DrawerHeader>

          <DrawerBody>
            <div className='py-12'>
              <p className='mb-16'>
                Width is set as "50%". Drag the left edge to resize - it will convert to pixels.
              </p>
              <ContentPlaceholder height={300} />
            </div>
          </DrawerBody>
        </DrawerContent>
      </Drawer>
    </VStack>
  );
};
```

### Resizable with overflow list

Resizing with content that reflows, which is the case worth testing: the panel is only useful
at its new width if what is inside adapts.

```tsx
import { useState } from 'react';
import { Attribute, AttributeLabel, AttributeValue } from '@wallarm-org/design-system/Attribute';
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerResizeHandle, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';
import { OverflowList } from '@wallarm-org/design-system/OverflowList';
import { Tag } from '@wallarm-org/design-system/Tag';

export const ResizableWithOverflowList = () => {
  const [open, setOpen] = useState(false);

  return (
    <Drawer data-testid='drawer' width={480} open={open} onOpenChange={setOpen}>
      <DrawerTrigger asChild>
        <Button variant='outline' color='neutral'>
          Open Resizable Drawer with OverflowList
        </Button>
      </DrawerTrigger>
      <DrawerContent>
        <DrawerResizeHandle />
        <DrawerHeader>
          <DrawerTitle>Resizable Drawer with OverflowList</DrawerTitle>
        </DrawerHeader>
        <DrawerBody>
          <p className='mb-16'>Drag the left edge — the tag list reflows live.</p>
          <Attribute>
            <AttributeLabel>Attack types</AttributeLabel>
            <AttributeValue>
              <OverflowList
                data-testid='tags-overflow-list'
                className='gap-4'
                items={DRAWER_TAGS}
                itemRenderer={item => (
                  <Tag key={item} data-testid='tag'>
                    {item}
                  </Tag>
                )}
              />
            </AttributeValue>
          </Attribute>
        </DrawerBody>
      </DrawerContent>
    </Drawer>
  );
};
```

### Scrollable

The body scrolls between a fixed header and footer, so the actions never scroll away from the
work.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';

export const Scrollable = () => {
  return (
    <Drawer>
      <DrawerTrigger asChild>
        <Button variant='outline' color='neutral'>
          Open Drawer with Scroll
        </Button>
      </DrawerTrigger>

      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>Scrollable Content</DrawerTitle>
        </DrawerHeader>

        <DrawerBody>
          <div className='flex flex-col gap-16 py-12'>
            {Array.from({ length: 20 }).map((_, i) => (
              <ContentPlaceholder key={i} height={100} />
            ))}
          </div>
        </DrawerBody>

        <DrawerFooter>
          <DrawerClose asChild>
            <Button variant='ghost' color='neutral' size='large'>
              Close
            </Button>
          </DrawerClose>
          <Button variant='primary' color='brand' size='large'>
            Confirm
          </Button>
        </DrawerFooter>
      </DrawerContent>
    </Drawer>
  );
};
```

### Controlled

Owning `open`, for a panel something outside it opens or closes.

```tsx
import { useState } from 'react';
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerFooter, DrawerHeader, DrawerTitle } from '@wallarm-org/design-system/Drawer';

export const Controlled = () => {
  const [open, setOpen] = useState(false);

  return (
    <>
      <Button variant='outline' color='neutral' onClick={() => setOpen(true)}>
        Open Controlled Drawer
      </Button>

      <Drawer open={open} onOpenChange={setOpen}>
        <DrawerContent>
          <DrawerHeader>
            <DrawerTitle>Controlled Drawer</DrawerTitle>
          </DrawerHeader>

          <DrawerBody>
            <div className='py-12'>
              <p className='mb-16'>This drawer is controlled externally.</p>
              <ContentPlaceholder />
            </div>
          </DrawerBody>

          <DrawerFooter>
            <DrawerClose asChild>
              <Button variant='ghost' color='neutral' size='large'>
                Cancel
              </Button>
            </DrawerClose>
            <Button variant='primary' color='brand' size='large'>
              Save Changes
            </Button>
          </DrawerFooter>
        </DrawerContent>
      </Drawer>
    </>
  );
};
```

### No closable on esc

Escape disabled, for a panel holding unsaved work where a reflex keystroke would lose it.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';

export const NoClosableOnEsc = () => {
  return (
    <Drawer closeOnEscape={false}>
      <DrawerTrigger asChild>
        <Button variant='outline' color='neutral'>
          Open Drawer
        </Button>
      </DrawerTrigger>

      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>Drawer Title</DrawerTitle>
        </DrawerHeader>

        <DrawerBody>
          <ContentPlaceholder fillHeight />
        </DrawerBody>
      </DrawerContent>
    </Drawer>
  );
};
```

### No overlay

Without the backdrop, which is how a drawer reads as non-blocking. Pair it with
`modal={false}` if the page behind should also stay usable.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';

export const NoOverlay = () => {
  return (
    <Drawer overlay={false}>
      <DrawerTrigger asChild>
        <Button variant='outline' color='neutral'>
          Open without Overlay
        </Button>
      </DrawerTrigger>

      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>No Overlay</DrawerTitle>
        </DrawerHeader>

        <DrawerBody>
          <div className='py-12'>
            <p>This drawer has no overlay backdrop.</p>
          </div>
        </DrawerBody>
      </DrawerContent>
    </Drawer>
  );
};
```

### With nested

A drawer from a drawer: same family, so the outer one steps back and the depth is legible.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';
import { PanelRight } from '@wallarm-org/design-system/icons';
import { VStack } from '@wallarm-org/design-system/Stack';
import { Text } from '@wallarm-org/design-system/Text';

export const WithNested = () => {
  return (
    <Drawer>
      <DrawerTrigger asChild>
        <Button variant='outline' color='neutral'>
          1st level drawer
        </Button>
      </DrawerTrigger>

      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>[Level 1] Main Drawer</DrawerTitle>

          {/* Level 2 drawer */}
          <Drawer>
            <DrawerTrigger asChild>
              <Button variant='ghost' color='neutral' size='small'>
                <PanelRight />
                2nd level drawer
              </Button>
            </DrawerTrigger>

            <DrawerContent>
              <DrawerHeader>
                <DrawerTitle>[Level 2] Detail View</DrawerTitle>
                {/* Level 3 drawer */}
                <Drawer>
                  <DrawerTrigger asChild>
                    <Button variant='ghost' color='neutral' size='small'>
                      <PanelRight />
                      3nd level drawer
                    </Button>
                  </DrawerTrigger>

                  <DrawerContent>
                    <DrawerHeader>
                      <DrawerTitle>[Level 3] Deep Nested</DrawerTitle>
                    </DrawerHeader>

                    <DrawerBody>
                      <VStack gap={12} align='start'>
                        <Text>Level 3! Unlimited nesting works.</Text>
                        <ContentPlaceholder height={150} />
                      </VStack>
                    </DrawerBody>

                    <DrawerFooter>
                      <DrawerClose asChild>
                        <Button size='large' variant='ghost' color='neutral'>
                          Back
                        </Button>
                      </DrawerClose>
                    </DrawerFooter>
                  </DrawerContent>
                </Drawer>
              </DrawerHeader>

              <DrawerBody>
                <VStack gap={12} align='start'>
                  <Text>
                    This is Level 2 drawer. Main drawer is pushed back. You can go deeper:
                  </Text>

                  <ContentPlaceholder height={150} />
                </VStack>
              </DrawerBody>

              <DrawerFooter>
                <DrawerClose asChild>
                  <Button variant='ghost' color='neutral' size='large'>
                    Back
                  </Button>
                </DrawerClose>
                <Button variant='primary' color='brand' size='large'>
                  Apply
                </Button>
              </DrawerFooter>
            </DrawerContent>
          </Drawer>
        </DrawerHeader>

        <DrawerBody>
          <VStack gap={12} align='start'>
            <Text>This is Level 1 drawer. Click below to open nested drawers:</Text>

            <ContentPlaceholder height={300} />
          </VStack>
        </DrawerBody>

        <DrawerFooter>
          <DrawerClose asChild>
            <Button variant='ghost' color='neutral' size='large'>
              Cancel
            </Button>
          </DrawerClose>
          <Button variant='primary' color='brand' size='large'>
            Save
          </Button>
        </DrawerFooter>
      </DrawerContent>
    </Drawer>
  );
};
```

### With nested dialog

A dialog opened from a drawer — different family, so the drawer stays put rather than
scaling. That separation is what `kind` exists for.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Dialog, DialogBody, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@wallarm-org/design-system/Dialog';
import { Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';
import { VStack } from '@wallarm-org/design-system/Stack';
import { Text } from '@wallarm-org/design-system/Text';

export const WithNestedDialog = () => {
  return (
    <Drawer>
      <DrawerTrigger asChild>
        <Button variant='outline' color='neutral'>
          Open drawer with dialog inside
        </Button>
      </DrawerTrigger>

      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>Drawer stays in place</DrawerTitle>
        </DrawerHeader>

        <DrawerBody>
          <VStack gap={12} align='start'>
            <Text>Opening the dialog below must not scale or shift this drawer.</Text>

            <Dialog>
              <DialogTrigger asChild>
                <Button variant='outline' color='neutral'>
                  Open dialog
                </Button>
              </DialogTrigger>

              <DialogContent>
                <DialogHeader>
                  <DialogTitle>Dialog over a drawer</DialogTitle>
                </DialogHeader>

                <DialogBody>
                  <ContentPlaceholder height={150} />
                </DialogBody>
              </DialogContent>
            </Dialog>
          </VStack>
        </DrawerBody>
      </DrawerContent>
    </Drawer>
  );
};
```

### With nested select

A `Select` inside a drawer, checking its menu escapes the panel instead of being clipped.

```tsx
import { createListCollection } from '@ark-ui/react/collection';
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@wallarm-org/design-system/DropdownMenu';
import { PanelRight } from '@wallarm-org/design-system/icons';
import { Select, SelectButton, SelectContent, SelectOption, SelectOptionIndicator, SelectOptionText, SelectPositioner } from '@wallarm-org/design-system/Select';
import { VStack } from '@wallarm-org/design-system/Stack';

export const WithNestedSelect = () => {
  const collection = createListCollection({
    items: [
      { label: 'React', value: 'react' },
      { label: 'Vue', value: 'vue' },
      { label: 'Angular', value: 'angular' },
    ],
  });

  return (
    <Drawer>
      <DrawerTrigger asChild>
        <Button variant='outline' color='neutral'>
          Open drawer with nested select
        </Button>
      </DrawerTrigger>

      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>[Level 1] Main Drawer</DrawerTitle>

          <Drawer>
            <DrawerTrigger asChild>
              <Button variant='ghost' color='neutral' size='small'>
                <PanelRight />
                Open nested drawer
              </Button>
            </DrawerTrigger>

            <DrawerContent>
              <DrawerHeader>
                <DrawerTitle>[Level 2] With Select</DrawerTitle>
              </DrawerHeader>

              <DrawerBody>
                <VStack gap={12} align='start'>
                  <Select collection={collection} data-testid='nested-select'>
                    <SelectButton />

                    <SelectPositioner>
                      <SelectContent>
                        {collection.items.map(item => (
                          <SelectOption key={item.value} item={item}>
                            <SelectOptionText>{item.label}</SelectOptionText>
                            <SelectOptionIndicator />
                          </SelectOption>
                        ))}
                      </SelectContent>
                    </SelectPositioner>
                  </Select>

                  <DropdownMenu data-testid='nested-dropdown'>
                    <DropdownMenuTrigger asChild>
                      <Button variant='outline' color='neutral'>
                        Open menu
                      </Button>
                    </DropdownMenuTrigger>
                    <DropdownMenuContent>
                      <DropdownMenuItem>First action</DropdownMenuItem>
                      <DropdownMenuItem>Second action</DropdownMenuItem>
                    </DropdownMenuContent>
                  </DropdownMenu>
                </VStack>
              </DrawerBody>
            </DrawerContent>
          </Drawer>
        </DrawerHeader>

        <DrawerBody>
          <ContentPlaceholder height={200} />
        </DrawerBody>
      </DrawerContent>
    </Drawer>
  );
};
```

### With tabs

Tabs inside a drawer, which suits a panel with several facets of one subject.

```tsx
import { useState } from 'react';
import { Button } from '@wallarm-org/design-system/Button';
import { Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@wallarm-org/design-system/Tabs';

export const WithTabs = () => {
  const [activeTab, setActiveTab] = useState('tab1');

  return (
    <Drawer width={960}>
      <DrawerTrigger asChild>
        <Button variant='outline' color='neutral'>
          Open Drawer with Tabs
        </Button>
      </DrawerTrigger>

      <DrawerContent>
        <Tabs value={activeTab} onChange={setActiveTab} asChild>
          <DrawerHeader>
            <DrawerTitle>Tabbed Content</DrawerTitle>
          </DrawerHeader>

          <TabsList>
            <TabsTrigger value='tab1'>General</TabsTrigger>
            <TabsTrigger value='tab2'>Settings</TabsTrigger>
            <TabsTrigger value='tab3'>Advanced</TabsTrigger>
          </TabsList>

          <DrawerBody>
            <div className='pt-12'>
              <TabsContent value='tab1'>
                <h3 className='text-lg font-medium mb-8'>General Settings</h3>
                <ContentPlaceholder height={300} />
              </TabsContent>
              <TabsContent value='tab2'>
                <h3 className='text-lg font-medium mb-8'>Configuration</h3>
                <ContentPlaceholder height={300} />
              </TabsContent>
              <TabsContent value='tab3'>
                <h3 className='text-lg font-medium mb-8'>Advanced Options</h3>
                <ContentPlaceholder height={300} />
              </TabsContent>
            </div>
          </DrawerBody>

          <DrawerFooter>
            <DrawerClose asChild>
              <Button variant='ghost' color='neutral' size='large'>
                Cancel
              </Button>
            </DrawerClose>
            <Button variant='primary' color='brand' size='large'>
              Apply Settings
            </Button>
          </DrawerFooter>
        </Tabs>
      </DrawerContent>
    </Drawer>
  );
};
```

## Props

| Prop                | Type                                            | Default  | Description                                                                                                                                                                                                                                                                            |
| ------------------- | ----------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| kind                | `DrawerKind \| undefined`                       | `drawer` | Which overlay family this root belongs to. Drives the same-kind nesting counter behind the pushed-back animation: a Drawer only pushes back for a nested Drawer, a Dialog only for a nested Dialog. Set to 'dialog' by the Dialog component — consumers should not need to touch this. |
| open                | `boolean \| undefined`                          |          | Controlled open state                                                                                                                                                                                                                                                                  |
| onOpenChange        | `((open: boolean) => void) \| undefined`        |          | Controlled open change handler                                                                                                                                                                                                                                                         |
| closeOnEscape       | `boolean \| undefined`                          | `true`   | Whether ESC key closes the drawer (default: true)                                                                                                                                                                                                                                      |
| closeOnOutsideClick | `boolean \| undefined`                          | `true`   | Whether clicking outside closes the drawer (default: true)                                                                                                                                                                                                                             |
| overlay             | `boolean \| undefined`                          | `true`   | Overlay visibility (default: true)                                                                                                                                                                                                                                                     |
| modal               | `boolean \| undefined`                          | `true`   | Whether the drawer is modal — blocks interaction with the rest of the page (default: true)                                                                                                                                                                                             |
| width               | `string \| number \| undefined`                 |          | Width - number for pixels, string for percentage (e.g., "50%")                                                                                                                                                                                                                         |
| minWidth            | `number \| undefined`                           |          | Minimum width in pixels                                                                                                                                                                                                                                                                |
| maxWidth            | `number \| undefined`                           |          | Maximum width in pixels                                                                                                                                                                                                                                                                |
| onInteractOutside   | `complex`                                       |          | Fired when the user interacts outside the drawer (e.g. clicks the backdrop).                                                                                                                                                                                                           |
| onEscapeKeyDown     | `((event: KeyboardEvent) => void) \| undefined` |          | Fired when the user presses the Escape key while the drawer is open.                                                                                                                                                                                                                   |
| data-testid         | `string \| undefined`                           |          |                                                                                                                                                                                                                                                                                        |

### DrawerClose

| Prop           | Type                                                 | Default | Description               |
| -------------- | ---------------------------------------------------- | ------- | ------------------------- |
| asChild        | `boolean \| undefined`                               | `false` | Render as child component |
| title          | `string \| undefined`                                |         |                           |
| defaultChecked | `boolean \| undefined`                               |         |                           |
| defaultValue   | `string \| number \| readonly string[] \| undefined` |         |                           |
| autoFocus      | `boolean \| undefined`                               |         |                           |
| id             | `string \| undefined`                                |         |                           |
| tabIndex       | `number \| undefined`                                |         |                           |
| rel            | `string \| undefined`                                |         |                           |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`        |         |                           |
| type           | `"button" \| "submit" \| "reset" \| undefined`       |         |                           |
| disabled       | `boolean \| undefined`                               |         |                           |
| name           | `string \| undefined`                                |         |                           |
| value          | `string \| number \| readonly string[] \| undefined` |         |                           |
| data-testid    | `string \| undefined`                                |         |                           |

### DrawerContent

| Prop    | Type                   | Default | Description |
| ------- | ---------------------- | ------- | ----------- |
| asChild | `boolean \| undefined` |         |             |

### DrawerFooterControls

| Prop      | Type                               | Default | Description |
| --------- | ---------------------------------- | ------- | ----------- |
| placement | `{ left: string; right: string; }` | `right` |             |

### DrawerPositioner

| Prop          | Type      | Default | Description |
| ------------- | --------- | ------- | ----------- |
| isResizing \* | `boolean` |         |             |

### DrawerTrigger

| Prop           | Type                                                 | Default | Description                                                                   |
| -------------- | ---------------------------------------------------- | ------- | ----------------------------------------------------------------------------- |
| asChild        | `boolean \| undefined`                               | `false` | Render as child component                                                     |
| value          | `string \| undefined`                                |         | Native button value attribute (string only, matching Ark UI's Dialog.Trigger) |
| title          | `string \| undefined`                                |         |                                                                               |
| defaultChecked | `boolean \| undefined`                               |         |                                                                               |
| defaultValue   | `string \| number \| readonly string[] \| undefined` |         |                                                                               |
| autoFocus      | `boolean \| undefined`                               |         |                                                                               |
| id             | `string \| undefined`                                |         |                                                                               |
| tabIndex       | `number \| undefined`                                |         |                                                                               |
| rel            | `string \| undefined`                                |         |                                                                               |
| onChange       | `ChangeEventHandler<T, Element> \| undefined`        |         |                                                                               |
| type           | `"button" \| "submit" \| "reset" \| undefined`       |         |                                                                               |
| disabled       | `boolean \| undefined`                               |         |                                                                               |
| name           | `string \| undefined`                                |         |                                                                               |
| data-testid    | `string \| undefined`                                |         |                                                                               |

## Explorer

