Documentation

Installation

npm install @01-collective/drawer bits-ui

Setup

Wrap your app content in a data-svelte-drawer-drawer-wrapper element. In SvelteKit, add it to app.html:

<body>
  <div data-svelte-drawer-drawer-wrapper="">
    %sveltekit.body%
  </div>
</body>

This wrapper is required for the background scaling effect. If you don't use shouldScaleBackground, it's still recommended for consistency.

Basic Usage

<script>
  import { Drawer } from '@01-collective/drawer';
</script>

<Drawer.Root>
  <Drawer.Trigger>Open</Drawer.Trigger>
  <Drawer.Portal>
    <Drawer.Overlay />
    <Drawer.Content>
      <Drawer.Handle />
      <Drawer.Title>Title</Drawer.Title>
      <Drawer.Description>Description</Drawer.Description>
      <Drawer.Close>Close</Drawer.Close>
    </Drawer.Content>
  </Drawer.Portal>
</Drawer.Root>

Components

ComponentDescription
Drawer.RootState manager. Wraps everything.
Drawer.TriggerButton that opens the drawer.
Drawer.PortalRenders content in a portal.
Drawer.OverlayBackdrop behind the drawer.
Drawer.ContentThe drawer panel. Handles drag events.
Drawer.HandleDrag handle with snap point cycling on tap.
Drawer.CloseButton that closes the drawer.
Drawer.TitleAccessible title (required for a11y).
Drawer.DescriptionAccessible description.
Drawer.NestedRootRoot for nested drawers.

Props — Drawer.Root

PropTypeDefaultDescription
openbooleanfalseControlled open state (bindable)
onOpenChange(open: boolean) => voidCalled when open state changes
direction'top' | 'bottom' | 'left' | 'right''bottom'Drawer direction
snapPoints(number | string)[]Snap points as fractions (0–1) or px strings
fadeFromIndexnumberlastOverlay fades from this snap point index
closeThresholdnumber0.25Fraction of height to trigger close
dismissiblebooleantrueWhether the drawer can be closed by user
modalbooleantrueModal mode with focus trap & overlay
handleOnlybooleanfalseOnly allow dragging via Handle
shouldScaleBackgroundbooleanfalseScale background wrapper on open
nestedbooleanfalseIs this a nested drawer
noBodyStylesbooleanfalseDon't apply body styles
fixedbooleanfalseOnly change height when keyboard opens
autoFocusbooleanfalseAuto-focus content on open
onDrag(event, progress) => voidCalled during drag
onRelease(event, open) => voidCalled on drag release
onClose() => voidCalled when drawer closes
onAnimationEnd(open: boolean) => voidCalled after open/close animation

Data Attributes

AttributeElementDescription
data-svelte-drawer-drawerContentMarks the drawer element
data-svelte-drawer-drawer-directionContenttop | bottom | left | right
data-svelte-drawer-overlayOverlayMarks the overlay
data-svelte-drawer-handleHandleMarks the handle
data-svelte-drawer-snap-pointsContent, Overlaytrue when snap points active
data-svelte-drawer-no-dragAnyPrevents drag on this element
data-stateContent, Overlayopen | closed

Styling

The component is headless — no styles are applied to visible elements. Use classes, inline styles, or target data attributes:

[data-svelte-drawer-overlay] {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
}

[data-svelte-drawer-drawer] {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: white;
  border-radius: 12px 12px 0 0;
}

The library ships CSS for animations and transitions on [data-svelte-drawer-drawer] and [data-svelte-drawer-overlay]. These are bundled automatically when you import any component.