motif svelteArc

Drawer ported

A temporary side surface for focused work.

Overlay and panel with a titled header that doubles as the drag handle, a close button, and a scrolling body. Built on Bits UI Dialog.

Live specimen

Loading demo…

Guidance

When to use

  • Side panels for filters, settings, or record details that keep the page in context.
  • Forms that are too long for a dialog but should not leave the current view.
  • Panels from any edge, via side, with drag-to-dismiss on the header.

When not to use

  • Use dialog for short decisions and confirmations.
  • Use bottom-sheet for mobile-first sheets with snap points.
  • Use popover for small anchored content that does not need a modal overlay.

Installation

pnpm dlx shadcn-svelte@latest add @arcui/drawer

Usage

<script lang="ts">
  import { Drawer, DrawerTrigger, DrawerContent, DrawerClose } from '$lib/components/drawer';

  let open = $state(false);
</script>

<Drawer bind:open>
  <DrawerTrigger class="btn-primary">Open Filters</DrawerTrigger>

  <DrawerContent title="Project Filters" description="Narrow the list of active repositories.">
    <div class="filters-body">
      <p>Filter options and configuration settings go here.</p>
    </div>
    <div class="filters-footer">
      <DrawerClose class="btn-secondary">Apply Filters</DrawerClose>
    </div>
  </DrawerContent>
</Drawer>

Variants & Examples

Right Slide-over

Default edge placement for inspector panels, record detail views, and edit forms.

<DrawerContent title="Settings" side="right">
  <!-- Content -->
</DrawerContent>

Left Navigation Drawer

Ideal for navigation drawers and hierarchy trees on smaller viewports.

<DrawerContent title="Navigation" side="left">
  <!-- Content -->
</DrawerContent>

Bottom Panel

Full-width bottom panel anchored to the bottom edge with centered grab bar.

<DrawerContent title="Console Output" side="bottom">
  <!-- Content -->
</DrawerContent>

API Reference

PropTypeDefaultDescription
openbooleanundefinedControlled open state; supports two-way bind:open.
defaultOpenbooleanfalseInitial state when uncontrolled.
onOpenChange(open: boolean) => void–Callback fired when the drawer opens or closes.
titlestring–Dialog title rendered in the drawer header, linked to Dialog.Title.
descriptionstring–Supporting text rendered under the title, linked to Dialog.Description.
side'left' | 'right' | 'top' | 'bottom''right'Edge the panel attaches to and slides from.
containerHTMLElement | nullnullRenders the drawer inside this container element instead of the document body.
classstring–Additional CSS class applied to the drawer panel.

Keyboard Interactions

KeyAction
EscapeCloses the drawer and returns focus to the trigger button.
Tab / Shift+TabTraps and cycles focus within the drawer content while open.

Accessibility

  • Built on Bits UI Dialog: provides role="dialog", aria-modal="true", and focus management.
  • Title and description are wired to Dialog.Title and Dialog.Description for screen readers.
  • Header close button is explicitly labelled "Close drawer".
  • Focus is trapped while open and restored to trigger on dismissal.

Motion

Phase 1 still-state: Panel animates from its designated edge using CSS keyframes (drawer-in / drawer-out) and backdrop blur overlay. Phase 2 wires gesture drag-to-dismiss on header, rubber-banding past resting boundary, and velocity fling physics.

Notes for AI

  • Use for side panels with forms, filters, settings, or detail views that keep the page in context.
  • Compose Drawer as the root with DrawerTrigger, DrawerContent, and DrawerClose.
  • Control open with bind:open when the drawer needs to close after an async form submission.

Source of truth: registry/components/drawer/drawer.tsx