A drawer component for Svelte 5, inspired by Vaul.
npm install @abhivarde/svelte-drawer
<script>
import { Drawer, DrawerOverlay, DrawerContent } from '@abhivarde/svelte-drawer';
let open = $state(false);
</script>
<button onclick={() => open = true}>
Open Drawer
</button>
<Drawer bind:open>
<DrawerOverlay class="fixed inset-0 bg-black/40" />
<DrawerContent class="fixed bottom-0 left-0 right-0 bg-white rounded-t-lg p-4">
<h2>Drawer Content</h2>
<p>This is a drawer component.</p>
<button onclick={() => open = false}>Close</button>
</DrawerContent>
</Drawer>
<script>
import { Drawer, DrawerOverlay, DrawerContent } from '@abhivarde/svelte-drawer';
let open = $state(false);
</script>
<Drawer bind:open direction="right">
<DrawerOverlay class="fixed inset-0 bg-black/40" />
<DrawerContent class="fixed right-0 top-0 bottom-0 w-80 bg-white p-4">
<h2>Side Drawer</h2>
<button onclick={() => open = false}>Close</button>
</DrawerContent>
</Drawer>
<script>
import { Drawer, DrawerOverlay, DrawerContent } from '@abhivarde/svelte-drawer';
let open = $state(false);
function handleOpenChange(isOpen) {
console.log('Drawer is now:', isOpen ? 'open' : 'closed');
open = isOpen;
}
</script>
<Drawer bind:open onOpenChange={handleOpenChange}>
<DrawerOverlay class="fixed inset-0 bg-black/40" />
<DrawerContent class="fixed bottom-0 left-0 right-0 bg-white rounded-t-lg p-4">
<h2>Controlled Drawer</h2>
</DrawerContent>
</Drawer>
<script>
import { Drawer, DrawerOverlay, DrawerContent } from '@abhivarde/svelte-drawer';
let open = $state(false);
</script>
<!-- Disable Escape key -->
<Drawer bind:open closeOnEscape={false}>
<DrawerOverlay class="fixed inset-0 bg-black/40" />
<DrawerContent class="fixed bottom-0 left-0 right-0 bg-white rounded-t-lg p-4">
<h2>Cannot close with Escape</h2>
</DrawerContent>
</Drawer>
<!-- Disable focus trap -->
<Drawer bind:open>
<DrawerOverlay class="fixed inset-0 bg-black/40" />
<DrawerContent trapFocus={false} class="fixed bottom-0 left-0 right-0 bg-white rounded-t-lg p-4">
<h2>Tab navigation not restricted</h2>
</DrawerContent>
</Drawer>
<script>
import { Drawer, DrawerOverlay, DrawerVariants } from '@abhivarde/svelte-drawer';
let open = $state(false);
</script>
<!-- Sheet variant (iOS-style bottom sheet) -->
<Drawer bind:open>
<DrawerOverlay class="fixed inset-0 bg-black/40" />
<DrawerVariants variant="sheet">
<div class="p-6">
<h2>iOS-style Sheet</h2>
<p>Clean and modern bottom sheet design</p>
</div>
</DrawerVariants>
</Drawer>
<!-- Dialog variant (center modal) -->
<Drawer bind:open>
<DrawerOverlay class="fixed inset-0 bg-black/40" />
<DrawerVariants variant="dialog">
<div class="p-6">
<h2>Dialog Style</h2>
<p>Center-positioned modal dialog</p>
</div>
</DrawerVariants>
</Drawer>
<!-- Sidebar variant (navigation drawer) -->
<Drawer bind:open direction="right">
<DrawerOverlay class="fixed inset-0 bg-black/40" />
<DrawerVariants variant="sidebar">
<div class="p-6">
<h2>Sidebar Navigation</h2>
<p>Side navigation drawer</p>
</div>
</DrawerVariants>
</Drawer>
Available variants for DrawerVariants component:
default - Standard bottom drawer with gray backgroundsheet - iOS-style bottom sheet (white, rounded, 85vh height)dialog - Center modal dialog styleminimal - Simple bottom drawer without extra stylingsidebar - Side navigation drawer (full height)closeOnEscape={false})Main wrapper component that manages drawer state and animations.
Props:
open (boolean, bindable) - Controls the open/closed stateonOpenChange (function, optional) - Callback when open state changesdirection ('bottom' | 'top' | 'left' | 'right', default: 'bottom') - Direction from which drawer slidescloseOnEscape (boolean, optional, default: true) - Whether Escape key closes the drawerOverlay component that appears behind the drawer.
Props:
class (string, optional) - CSS classes for stylingContent container for the drawer.
Props:
class (string, optional) - CSS classes for stylingtrapFocus (boolean, optional, default: true) - Whether to trap focus inside drawerPre-styled drawer content with built-in variants.
Props:
variant ('default' | 'sheet' | 'dialog' | 'minimal' | 'sidebar', default: 'default') - Preset style variantclass (string, optional) - Additional CSS classes for stylingtrapFocus (boolean, optional, default: true) - Whether to trap focus inside drawerVisit drawer.abhivarde.in to see live examples.
This project is licensed under the MIT License.
See the LICENSE file for details.
Inspired by Vaul by Emil Kowalski.