Current collected state Current
Observed since 20 Oct, 21:54 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Svelte 5 drawer/bottom-sheet component with native drag gestures, inspired by Vaul
A Svelte 5 drawer component — inspired by Vaul. A drawer/bottom-sheet component with native-feeling drag gestures. Built on top of Bits UI Dialog.
npm install @01-collective/drawer-svelte
Wrap your app content in a data-svelte-drawer-drawer-wrapper element (typically in your root layout or app.html):
<body>
<div data-svelte-drawer-drawer-wrapper="">
%sveltekit.body%
</div>
</body>
Then use the drawer:
<script>
import { Drawer } from '@01-collective/drawer-svelte';
</script>
<Drawer.Root>
<Drawer.Trigger>Open</Drawer.Trigger>
<Drawer.Portal>
<Drawer.Overlay class="overlay" />
<Drawer.Content class="content">
<Drawer.Handle />
<Drawer.Title>Title</Drawer.Title>
<Drawer.Description>Description</Drawer.Description>
<Drawer.Close>Close</Drawer.Close>
</Drawer.Content>
</Drawer.Portal>
</Drawer.Root>
bottom, top, left, right| Component | Description |
|---|---|
Drawer.Root | State manager, wraps everything |
Drawer.Trigger | Button that opens the drawer |
Drawer.Portal | Renders content in a portal |
Drawer.Overlay | Backdrop behind the drawer |
Drawer.Content | The drawer panel (handles drag events) |
Drawer.Handle | Drag handle with snap point cycling |
Drawer.Close | Button that closes the drawer |
Drawer.Title | Accessible title |
Drawer.Description | Accessible description |
Drawer.NestedRoot | Root for nested drawers |
Drawer.Root| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Controlled open state (bindable) |
onOpenChange | (open: boolean) => void | — | Called when open state changes |
direction | 'top' | 'bottom' | 'left' | 'right' | 'bottom' | Drawer direction |
snapPoints | (number | string)[] | — | Snap points as fractions (0-1) or px strings |
fadeFromIndex | number | last snap point | Overlay fades from this snap point index |
activeSnapPoint | number | string | null | — | Controlled active snap point |
setActiveSnapPoint | (point) => void | — | Callback when snap point changes |
closeThreshold | number | 0.25 | Fraction of height to trigger close |
dismissible | boolean | true | Whether the drawer can be closed |
modal | boolean | true | Modal mode with focus trap |
handleOnly | boolean | false | Only allow dragging via Handle |
shouldScaleBackground | boolean | false | Scale background on open |
nested | boolean | false | Is this a nested drawer |
noBodyStyles | boolean | false | Don't apply body styles |
fixed | boolean | false | Only change height on keyboard open |
autoFocus | boolean | false | Auto-focus content on open |
onDrag | (event, progress) => void | — | Called during drag |
onRelease | (event, open) => void | — | Called on drag release |
onClose | () => void | — | Called when drawer closes |
onAnimationEnd | (open: boolean) => void | — | Called after open/close animation |
<Drawer.Root snapPoints={[0.25, 0.5, 1]} fadeFromIndex={1}>
<Drawer.Trigger>Open</Drawer.Trigger>
<Drawer.Portal>
<Drawer.Overlay />
<Drawer.Content>
<Drawer.Handle />
<p>Drag to snap points at 25%, 50%, and 100%</p>
</Drawer.Content>
</Drawer.Portal>
</Drawer.Root>
<Drawer.Root direction="right">
<Drawer.Trigger>Open</Drawer.Trigger>
<Drawer.Portal>
<Drawer.Overlay />
<Drawer.Content>
<p>Slides in from the right</p>
</Drawer.Content>
</Drawer.Portal>
</Drawer.Root>
<Drawer.Root>
<Drawer.Trigger>Open Outer</Drawer.Trigger>
<Drawer.Portal>
<Drawer.Overlay />
<Drawer.Content>
<Drawer.NestedRoot>
<Drawer.Trigger>Open Inner</Drawer.Trigger>
<Drawer.Portal>
<Drawer.Overlay />
<Drawer.Content>
<p>Nested drawer content</p>
</Drawer.Content>
</Drawer.Portal>
</Drawer.NestedRoot>
</Drawer.Content>
</Drawer.Portal>
</Drawer.Root>
<Drawer.Root shouldScaleBackground>
<!-- ... -->
</Drawer.Root>
<script>
let open = $state(false);
</script>
<button onclick={() => open = true}>Open programmatically</button>
<Drawer.Root bind:open>
<Drawer.Portal>
<Drawer.Overlay />
<Drawer.Content>
<p>Controlled drawer</p>
</Drawer.Content>
</Drawer.Portal>
</Drawer.Root>
<Drawer.Root dismissible={false}>
<!-- User cannot drag-to-close or click overlay to close -->
</Drawer.Root>
<Drawer.Root modal={false}>
<!-- No overlay, no focus trap, interact with page behind -->
</Drawer.Root>
The component is headless. Style using classes, inline styles, or 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: 10px 10px 0 0;
}
| Attribute | Values | Element |
|---|---|---|
data-svelte-drawer-drawer | — | Content |
data-svelte-drawer-drawer-direction | top|bottom|left|right | Content |
data-svelte-drawer-overlay | — | Overlay |
data-svelte-drawer-handle | — | Handle |
data-svelte-drawer-snap-points | true|false | Content, Overlay |
data-svelte-drawer-drawer-visible | true|false | Handle |
data-svelte-drawer-no-drag | — | Any element (prevents drag) |
data-state | open|closed | Content, Overlay |
MIT
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.1.0Registry latest | 14 Mar 2026 | 15 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 21:54 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:0141bb67511dc39528e3d0bfc53213882b45a800c0618094d3ea35fb2c07a7c6