{"id":50838475,"url":"https://github.com/harshmandan/svaul","last_synced_at":"2026-06-14T05:33:49.283Z","repository":{"id":362883989,"uuid":"1260347843","full_name":"harshmandan/svaul","owner":"harshmandan","description":"Svelte 5 port of the amazing Vaul bottom drawer","archived":false,"fork":false,"pushed_at":"2026-06-07T18:07:19.000Z","size":111,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-14T05:33:48.477Z","etag":null,"topics":["component","drawer","svelte","sveltekit"],"latest_commit_sha":null,"homepage":"https://harshmandan.github.io/svaul/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/harshmandan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-05T11:56:06.000Z","updated_at":"2026-06-12T15:57:05.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/harshmandan/svaul","commit_stats":null,"previous_names":["harshmandan/sveet"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/harshmandan/svaul","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harshmandan%2Fsvaul","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harshmandan%2Fsvaul/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harshmandan%2Fsvaul/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harshmandan%2Fsvaul/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/harshmandan","download_url":"https://codeload.github.com/harshmandan/svaul/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harshmandan%2Fsvaul/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34310801,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-14T02:00:07.365Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["component","drawer","svelte","sveltekit"],"created_at":"2026-06-14T05:33:49.167Z","updated_at":"2026-06-14T05:33:49.268Z","avatar_url":"https://github.com/harshmandan.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n  \u003cimg src=\"./static/favicon.svg\" alt=\"svaul\" width=\"400\" /\u003e\n\n  \u003ch1\u003esvaul\u003c/h1\u003e\n\n[![version](https://img.shields.io/npm/v/@harshmandan/svaul.svg)](https://www.npmjs.com/package/@harshmandan/svaul)\n[![downloads](http://img.shields.io/npm/dm/@harshmandan/svaul.svg)](https://www.npmjs.com/package/@harshmandan/svaul)\n\n\u003c/div\u003e\n\nAn unstyled, **runes-first drawer / bottom-sheet for Svelte 5** — drag-to-dismiss, snap\npoints, nested drawers, and an accessible dialog core. **Zero runtime dependencies.**\n\nA ground-up Svelte 5 re-imagining of [vaul](https://github.com/emilkowalski/vaul) and\n[vaul-svelte](https://github.com/huntabyte/vaul-svelte) (see [Credits](#credits)). See\nthe advantages over both in the [Why svaul?](#why-svaul) section below.\n\n## Quick start\n\n```sh\nnpm i @harshmandan/svaul\n```\n\n\u003e Requires Svelte `^5`.\n\n```svelte\n\u003cscript lang=\"ts\"\u003e\n  import Drawer from \"@harshmandan/svaul\";\n  let open = $state(false);\n\u003c/script\u003e\n\n\u003cDrawer bind:open class=\"rounded-t-xl bg-white p-4\"\u003e\n  {#snippet trigger(props)}\n    \u003cbutton {...props}\u003eOpen\u003c/button\u003e\n  {/snippet}\n\n  \u003ch2\u003eA bottom sheet\u003c/h2\u003e\n  \u003cp\u003eDrag it down to dismiss, or tap outside.\u003c/p\u003e\n\n  {#snippet footer({ close })}\n    \u003cbutton onclick={close}\u003eClose\u003c/button\u003e\n  {/snippet}\n\u003c/Drawer\u003e\n```\n\nEvery part of the drawer is customizable via **named snippets** in the `\u003cDrawer\u003e` component:\n`trigger`, `overlay`, `content`, `handle`, `title`, `description`,\n`header`, `footer`, and the default `children` (the scrollable body). Supply only the parts\nyou want to own; each renders a sensible default otherwise. Body snippets receive\n`{ open, close, setOpen, closeAll, cycleSnapPoint }` controls.\n\nTo close the drawer on the browser back button, drive `open` yourself and reset it from\n`onOpenChange` + a `popstate` listener (svaul intentionally doesn't touch history).\n\n## Key props\n\n| Prop | Default | |\n|---|---|---|\n| `bind:open` | — | controlled/uncontrolled open state (also `onOpenChange`) |\n| `direction` | `\"bottom\"` | `top` · `bottom` · `left` · `right` |\n| `snapPoints` | — | fractions (`0.5`) or px (`\"148px\"`); pair with `bind:activeSnapPoint` |\n| `dismissible` | `true` | drag / overlay-click / Escape can close |\n| `modal` | `true` | overlay + scroll-lock + focus-trap |\n| `scaleBackground` | `false` | scale `[data-drawer-wrapper]` (the card-stack look) |\n| `handleOnly` | `false` | only the handle initiates a drag |\n| `dragSensitivity` | `1` | `\u003e1` makes the drawer move faster than the cursor |\n| `disableAnimation` | `false` | instant open / close |\n\nPlus `closeThreshold`, `repositionInputs`, `preventScrollRestoration`, `noBodyStyles`,\n`setBackgroundColorOnScale`, `backgroundColor`, `borderRadius`, `autoFocus`,\n`fadeFromIndex`, `snapToSequentialPoint`, `container`, `keepMounted`, `onlyPrimaryPointer`\n— see the exported types.\n\n**Snap points** accept fractions (`0.5`), pixels (`\"148px\"`), percentages (`\"50%\"`), and\n`calc()` combinations: `snapPoints={[\"calc(50% + 24px)\", 1]}`.\n\n## Styling\n\nIt ships unstyled — bring your own `class`. Everything is reachable via data attributes\n(`[data-drawer]`, `[data-drawer-overlay]`, `[data-drawer-handle]`) and CSS variables\n(`--drawer-bg`, `--drawer-overlay-bg`, `--drawer-handle-bg`, `--drawer-handle-gap`, …).\n\nThe library's own CSS lives in the **`svaul` cascade layer**, so your styles always win\nwithout `!important`. With Tailwind v4, declare the layer order once:\n\n```css\n@layer svaul;\n@import \"tailwindcss\";\n```\n\n\u003e **`scaleBackground` note:** wrap your page in `\u003cdiv data-drawer-wrapper\u003e` and give it an\n\u003e opaque background. The gap behind the lifted page is painted via `setBackgroundColorOnScale`\n\u003e (default black) — if the wrapper is transparent the whole page looks black.\n\n## Why svaul?\n\n- **Svelte 5 runes-native** — built on `$state`/`$derived`/`$effect`, `{@attach}` attachments\n  and snippets, not stores or `$:` side-effect chains.\n- **Zero runtime dependencies** — the portal, focus-trap, scroll-lock, dismiss/Escape, `inert`\n  background and ARIA are all hand-rolled. (vaul-svelte ships `bits-ui`; vaul is React-only.)\n- **Svelte Native API** — a single `\u003cDrawer\u003e` with named snippets *and* a headless `Drawer` class,\n  instead of compound `Drawer.Root/Content/…` parts.\n- **Bug fixes** Fixes bugs with both vaul and svelte-vaul: ref-counted scroll-lock \u0026 background-color\n  restore that **always** reverts, **topmost-only** outside-click for nested drawers, the\n  on-screen-keyboard \"drawer shoots off-screen\" fix, **pixel-snapped** snap offsets (no blurry\n  text), a dismiss-**blink** fix, `prefers-reduced-motion` honored on the scaled background, and\n  `modal` actually wired through. And more.\n\n## Credits\n\nThis library stands entirely on the shoulders of two projects:\n\n- **[vaul](https://github.com/emilkowalski/vaul)** by [Emil Kowalski](https://emilkowalski.com)\n  — the original React drawer. Its drag physics, snap-point math, and the overall feel are\n  ported from here.\n- **[vaul-svelte](https://github.com/huntabyte/vaul-svelte)** by\n  [Huntabyte](https://github.com/huntabyte) — the Svelte port that proved the idea. `svaul`\n  is a from-scratch Svelte 5 (runes, zero-dependency) take on the same concept.\n\nHuge thanks to both. ♥\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharshmandan%2Fsvaul","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fharshmandan%2Fsvaul","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharshmandan%2Fsvaul/lists"}