{"_id":"@01-collective/drawer-svelte","name":"@01-collective/drawer-svelte","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@01-collective/drawer-svelte","version":"0.1.0","description":"Svelte 5 drawer/bottom-sheet component with native drag gestures, inspired by Vaul","keywords":["svelte","svelte5","drawer","bottom-sheet","vaul","vaul-svelte","sheet","modal","dialog","bits-ui"],"license":"MIT","type":"module","svelte":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","svelte":"./dist/index.js"}},"scripts":{"dev":"vite dev","build":"vite build && npm run package","package":"svelte-kit sync && svelte-package","check":"svelte-kit sync && svelte-check --tsconfig ./tsconfig.json","test":"vitest","test:e2e":"playwright test"},"peerDependencies":{"svelte":"^5.0.0"},"dependencies":{"bits-ui":">=2.16.0"},"devDependencies":{"@playwright/test":"^1.50.0","@sveltejs/adapter-auto":"^6.0.0","@sveltejs/adapter-static":"^3.0.10","@sveltejs/kit":"^2.54.0","@sveltejs/package":"^2.5.7","@sveltejs/vite-plugin-svelte":"^6.2.4","@testing-library/jest-dom":"^6.9.1","@testing-library/svelte":"^5.3.1","bits-ui":"^2.16.3","jsdom":"^28.1.0","svelte":"^5.53.10","svelte-check":"^4.0.0","typescript":"^5.7.0","vite":"^6.0.0","vitest":"^3.0.0"},"gitHead":"01d833f1bc623cb84c502ec69caeba64607b4a68","_id":"@01-collective/drawer-svelte@0.1.0","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-ohHhw2QbIWxtmJ1k2bHgu6UYqbZnlF3a2UguQ4sEqMrr01M6WUYLnRfiJ7wN7NE7LFX3YuYaZQMeKLFiebKfqQ==","shasum":"0d3535424fe34e7f428624ec7b51bce49bbe48db","tarball":"https://registry.npmjs.org/@01-collective/drawer-svelte/-/drawer-svelte-0.1.0.tgz","fileCount":35,"unpackedSize":81247,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCSBBOUZXrvDKNxUaEfVS8KuWtSmcnMJvnD9mS70HJxJgIhAMemaxuFbyaTwCtKX9F2B9qK+7v0KrupjpXBJqDK5H55"}]},"_npmUser":{"name":"lemokami","email":"jesswinw@gmail.com"},"directories":{},"maintainers":[{"name":"lemokami","email":"jesswinw@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/drawer-svelte_0.1.0_1773483499886_0.6946775705035937"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-14T10:18:19.738Z","0.1.0":"2026-03-14T10:18:20.043Z","modified":"2026-03-14T10:18:20.273Z"},"maintainers":[{"name":"lemokami","email":"jesswinw@gmail.com"}],"description":"Svelte 5 drawer/bottom-sheet component with native drag gestures, inspired by Vaul","keywords":["svelte","svelte5","drawer","bottom-sheet","vaul","vaul-svelte","sheet","modal","dialog","bits-ui"],"license":"MIT","readme":"# @01-collective/drawer-svelte\n\nA Svelte 5 drawer component — inspired by [Vaul](https://github.com/emilkowalski/vaul). A drawer/bottom-sheet component with native-feeling drag gestures. Built on top of [Bits UI](https://bits-ui.com) Dialog.\n\n## Installation\n\n```bash\nnpm install @01-collective/drawer-svelte\n```\n\n## Quick Start\n\nWrap your app content in a `data-svelte-drawer-drawer-wrapper` element (typically in your root layout or `app.html`):\n\n```html\n<body>\n  <div data-svelte-drawer-drawer-wrapper=\"\">\n    %sveltekit.body%\n  </div>\n</body>\n```\n\nThen use the drawer:\n\n```svelte\n<script>\n  import { Drawer } from '@01-collective/drawer-svelte';\n</script>\n\n<Drawer.Root>\n  <Drawer.Trigger>Open</Drawer.Trigger>\n  <Drawer.Portal>\n    <Drawer.Overlay class=\"overlay\" />\n    <Drawer.Content class=\"content\">\n      <Drawer.Handle />\n      <Drawer.Title>Title</Drawer.Title>\n      <Drawer.Description>Description</Drawer.Description>\n      <Drawer.Close>Close</Drawer.Close>\n    </Drawer.Content>\n  </Drawer.Portal>\n</Drawer.Root>\n```\n\n## Features\n\n- **Drag to dismiss** — flick or drag past threshold to close\n- **Snap points** — multiple stop positions (fraction or px)\n- **All directions** — `bottom`, `top`, `left`, `right`\n- **Nested drawers** — drawers within drawers\n- **Background scaling** — iOS-style sheet effect\n- **Keyboard handling** — repositions inputs when virtual keyboard opens\n- **Mobile scroll lock** — prevents body scroll on iOS Safari\n- **Modal & non-modal** — with proper focus trapping\n- **Accessible** — built on Bits UI Dialog (WAI-ARIA compliant)\n- **Headless** — unstyled, bring your own CSS\n\n## Components\n\n| Component | Description |\n|-----------|-------------|\n| `Drawer.Root` | State manager, wraps everything |\n| `Drawer.Trigger` | Button that opens the drawer |\n| `Drawer.Portal` | Renders content in a portal |\n| `Drawer.Overlay` | Backdrop behind the drawer |\n| `Drawer.Content` | The drawer panel (handles drag events) |\n| `Drawer.Handle` | Drag handle with snap point cycling |\n| `Drawer.Close` | Button that closes the drawer |\n| `Drawer.Title` | Accessible title |\n| `Drawer.Description` | Accessible description |\n| `Drawer.NestedRoot` | Root for nested drawers |\n\n## Props\n\n### `Drawer.Root`\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `open` | `boolean` | `false` | Controlled open state (bindable) |\n| `onOpenChange` | `(open: boolean) => void` | — | Called when open state changes |\n| `direction` | `'top' \\| 'bottom' \\| 'left' \\| 'right'` | `'bottom'` | Drawer direction |\n| `snapPoints` | `(number \\| string)[]` | — | Snap points as fractions (0-1) or px strings |\n| `fadeFromIndex` | `number` | last snap point | Overlay fades from this snap point index |\n| `activeSnapPoint` | `number \\| string \\| null` | — | Controlled active snap point |\n| `setActiveSnapPoint` | `(point) => void` | — | Callback when snap point changes |\n| `closeThreshold` | `number` | `0.25` | Fraction of height to trigger close |\n| `dismissible` | `boolean` | `true` | Whether the drawer can be closed |\n| `modal` | `boolean` | `true` | Modal mode with focus trap |\n| `handleOnly` | `boolean` | `false` | Only allow dragging via Handle |\n| `shouldScaleBackground` | `boolean` | `false` | Scale background on open |\n| `nested` | `boolean` | `false` | Is this a nested drawer |\n| `noBodyStyles` | `boolean` | `false` | Don't apply body styles |\n| `fixed` | `boolean` | `false` | Only change height on keyboard open |\n| `autoFocus` | `boolean` | `false` | Auto-focus content on open |\n| `onDrag` | `(event, progress) => void` | — | Called during drag |\n| `onRelease` | `(event, open) => void` | — | Called on drag release |\n| `onClose` | `() => void` | — | Called when drawer closes |\n| `onAnimationEnd` | `(open: boolean) => void` | — | Called after open/close animation |\n\n## Examples\n\n### Snap Points\n\n```svelte\n<Drawer.Root snapPoints={[0.25, 0.5, 1]} fadeFromIndex={1}>\n  <Drawer.Trigger>Open</Drawer.Trigger>\n  <Drawer.Portal>\n    <Drawer.Overlay />\n    <Drawer.Content>\n      <Drawer.Handle />\n      <p>Drag to snap points at 25%, 50%, and 100%</p>\n    </Drawer.Content>\n  </Drawer.Portal>\n</Drawer.Root>\n```\n\n### Right Drawer\n\n```svelte\n<Drawer.Root direction=\"right\">\n  <Drawer.Trigger>Open</Drawer.Trigger>\n  <Drawer.Portal>\n    <Drawer.Overlay />\n    <Drawer.Content>\n      <p>Slides in from the right</p>\n    </Drawer.Content>\n  </Drawer.Portal>\n</Drawer.Root>\n```\n\n### Nested Drawers\n\n```svelte\n<Drawer.Root>\n  <Drawer.Trigger>Open Outer</Drawer.Trigger>\n  <Drawer.Portal>\n    <Drawer.Overlay />\n    <Drawer.Content>\n      <Drawer.NestedRoot>\n        <Drawer.Trigger>Open Inner</Drawer.Trigger>\n        <Drawer.Portal>\n          <Drawer.Overlay />\n          <Drawer.Content>\n            <p>Nested drawer content</p>\n          </Drawer.Content>\n        </Drawer.Portal>\n      </Drawer.NestedRoot>\n    </Drawer.Content>\n  </Drawer.Portal>\n</Drawer.Root>\n```\n\n### Background Scaling\n\n```svelte\n<Drawer.Root shouldScaleBackground>\n  <!-- ... -->\n</Drawer.Root>\n```\n\n### Controlled State\n\n```svelte\n<script>\n  let open = $state(false);\n</script>\n\n<button onclick={() => open = true}>Open programmatically</button>\n\n<Drawer.Root bind:open>\n  <Drawer.Portal>\n    <Drawer.Overlay />\n    <Drawer.Content>\n      <p>Controlled drawer</p>\n    </Drawer.Content>\n  </Drawer.Portal>\n</Drawer.Root>\n```\n\n### Prevent Closing\n\n```svelte\n<Drawer.Root dismissible={false}>\n  <!-- User cannot drag-to-close or click overlay to close -->\n</Drawer.Root>\n```\n\n### Non-modal\n\n```svelte\n<Drawer.Root modal={false}>\n  <!-- No overlay, no focus trap, interact with page behind -->\n</Drawer.Root>\n```\n\n## Styling\n\nThe component is headless. Style using classes, inline styles, or data attributes:\n\n```css\n[data-svelte-drawer-overlay] {\n  position: fixed;\n  inset: 0;\n  background: rgba(0, 0, 0, 0.4);\n}\n\n[data-svelte-drawer-drawer] {\n  position: fixed;\n  bottom: 0;\n  left: 0;\n  right: 0;\n  background: white;\n  border-radius: 10px 10px 0 0;\n}\n```\n\n### Data Attributes\n\n| Attribute | Values | Element |\n|-----------|--------|---------|\n| `data-svelte-drawer-drawer` | — | Content |\n| `data-svelte-drawer-drawer-direction` | `top\\|bottom\\|left\\|right` | Content |\n| `data-svelte-drawer-overlay` | — | Overlay |\n| `data-svelte-drawer-handle` | — | Handle |\n| `data-svelte-drawer-snap-points` | `true\\|false` | Content, Overlay |\n| `data-svelte-drawer-drawer-visible` | `true\\|false` | Handle |\n| `data-svelte-drawer-no-drag` | — | Any element (prevents drag) |\n| `data-state` | `open\\|closed` | Content, Overlay |\n\n## Credits\n\n- [Svelte Drawer](https://github.com/emilkowalski/svelte-drawer) by Emil Kowalski — original React implementation\n- [Bits UI](https://bits-ui.com) — accessible Svelte 5 primitives\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-a61b12c0e6e1ef0f851bbb430f6dfd38"}