npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@drawer-kit/svelte

v0.1.0

Published

A headless, primitive drawer/sidebar component for Svelte 5 — inline or overlay, collapsible, resizable, RTL-aware, zero runtime icon dependency.

Readme

@drawer-kit/svelte

A headless-friendly, primitive drawer/sidebar component for Svelte 5 (runes). Inline or overlay, collapsible to an icon rail, resizable, RTL-aware, SSR-safe, and stateful via a single DrawerController class you own and can share across components.

No runtime icon-library dependency — every icon slot in the package is a plain Svelte snippet, with a small dependency-free SVG as the fallback when you don't supply one.

Install

npm install @drawer-kit/svelte

Requires svelte ^5.20.0 as a peer dependency (needed for the $props.id() rune the popover uses internally for SSR-safe unique ids).

Quick start

<script lang="ts">
	import { Drawer, DrawerLayout, DrawerController } from '@drawer-kit/svelte';

	const nav = new DrawerController({
		variant: 'auto', // inline on desktop, overlay below the mobile breakpoint
		collapsible: true, // adds a third 'collapsed' (icon-rail) mode
		initialMode: 'open'
	});
</script>

<DrawerLayout controller={nav}>
	{#snippet panel()}
		<Drawer controller={nav} headerHeight={56}>
			{#snippet children(ctrl)}
				<!-- your nav content -->
			{/snippet}
		</Drawer>
	{/snippet}

	{#snippet children()}
		<!-- your page content -->
	{/snippet}
</DrawerLayout>

DrawerController is a plain class — construct it once (in module scope, a $state-holding parent component, or wherever fits your app) and pass the same instance to every element that needs to read or drive it.

Core concepts

  • Modes: 'closed' | 'collapsed' | 'open'. 'collapsed' requires collapsible: true and is inline-only — it's automatically cleared when the effective variant promotes to overlay.
  • Variant: 'inline' | 'overlay' | 'auto' (default 'auto': inline above the mobile breakpoint, overlay below it). effectiveVariant on the controller is always the resolved, renderable value.
  • Resizable: resizable: true + minWidth/maxWidth adds a drag-to-resize handle on the panel's open edge.
  • Scoped mounting: pass mountTarget to scope an overlay drawer (and its backdrop/edge button) to a specific container instead of the viewport.
  • RTL: every logical-direction detail (which physical edge a placement="start" panel renders on, which way it slides, which side its border sits on) is derived automatically — see placement and resolveOverlayTranslate/resolvePanelPositionClasses if you're building custom pieces on top of the primitives.

Components

| Component | Purpose | | --- | --- | | DrawerController | Owns all state and transition logic. Framework-agnostic aside from runes. | | DrawerLayout | Row wrapper: panel + content pane, with a scrollStrategy choice. | | Drawer | The panel itself: header, edge button, resize handle, scroll region. | | DrawerPanel / DrawerContent / DrawerBackdrop | Lower-level pieces Drawer/DrawerLayout compose — exported for custom layouts. | | DrawerEdgeButton | The open/collapse/close toggle rendered on the panel's edge. | | DrawerGroup | A nav section header/divider with an optional expand/collapse chevron. | | DrawerPopover | An anchored popover, positioned relative to any element (e.g. a collapsed-rail item). | | PrefixTile / ContentTile / NavigationSkeleton | Small nav-row building blocks (icon slot, animated label, loading placeholder). |

Every prop type, plus every pure decision/style-building function the components are built from (resolveToggleTarget, resolvePanelWidth, etc.) and every tunable constant (DEFAULT_OPEN_WIDTH, DRAWER_TRANSITION_MS, ...) is exported from the package root, so you can build a related custom piece without duplicating this package's logic.

Icon injection

Icon slots (the edge button's open/collapse/close icons, DrawerGroup's chevron, PrefixTile's leading icon) are plain Svelte snippets — the library has no icon-library dependency of its own:

<script lang="ts">
	import { Drawer } from '@drawer-kit/svelte';
	import { PanelLeftOpen, PanelLeftClose, X } from 'lucide-svelte';
</script>

<Drawer
	controller={nav}
	edgeButton={{
		icons: { open: openIcon, collapse: collapseIcon, close: closeIcon }
	}}
>
	{#snippet openIcon({ size })}<PanelLeftOpen {size} />{/snippet}
	{#snippet collapseIcon({ size })}<PanelLeftClose {size} />{/snippet}
	{#snippet closeIcon({ size })}<X {size} />{/snippet}
	{#snippet children(ctrl)}...{/snippet}
</Drawer>

Omit any of these and the package falls back to its own tiny dependency-free inline SVG — same position, same behavior, no bundle-size cost for consumers who don't need an icon library. Because these are ordinary snippets, they compose like any other Svelte value: a snippet can itself render another snippet passed into it (see the "Icon injection" demo in examples/svelte for a badge-wrapped icon built this way) — the library places no restriction on nesting.

Native scroll restoration vs. resetScroll()

DrawerLayout's content pane is its own scroll container by default (scrollStrategy="container"), which is invisible to your router's window-level scroll restoration. Prefer scrollStrategy="document" when your app's root layout can let the document itself scroll — restoration then works natively, for free.

When it can't, call the exported resetScroll() method from your own router's "navigation happened" hook — the package deliberately has no router dependency of its own:

<script lang="ts">
	import { afterNavigate } from '$app/navigation'; // SvelteKit example
	let layout: ReturnType<typeof DrawerLayout> | undefined;
	afterNavigate(() => layout?.resetScroll());
</script>

<DrawerLayout controller={nav} bind:this={layout}>...</DrawerLayout>

Breaking change (unreleased): the resetScrollOnNavigate prop (a SvelteKit-only, $app/navigation-coupled implementation of this) has been removed in favor of the router-agnostic resetScroll() method above — the old prop made the package fail to build for any non-SvelteKit consumer.

Accessibility

  • The edge button and backdrop both compute their aria-label/action from the controller's current mode — override via labels.
  • DrawerPopover uses $props.id() for its aria-describedby/ aria-controls wiring, so ids stay stable across SSR and hydration.
  • All logical-direction/RTL handling described above applies to focus order and swipe/slide direction too, not just layout.

Example

A runnable demo lives in examples/svelte — see that directory's README for how to run it.

Full API reference

src/docs/Drawer.md in this repository has the complete prop-by-prop reference for every component (not included in the published npm package — clone the repo or browse it on GitHub).