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

kenspeckle

v0.1.4

Published

Svelte 5 utilities under one naming convention: attachments first-class, view transitions, and a state machine with context.

Readme

Svelte utilities you recognize at sight. One naming convention, no use prefix, attachments first-class.

The sentence test

Every export earns its shape by being said aloud: "I want a new ___."

  • "a new finite state machine" — speakable, and you drive it with methods → new FiniteStateMachine()
  • "a new element size" — not a thing you ask for → elementSize()
  • "a new is idle" — nonsense → idle()

Two rules fall out:

  1. Class only when "new X" is speakable and you drive it with methods. There are exactly two: FiniteStateMachine and StateHistory.
  2. Everything else is a camelCase function. No use prefix — that's a React Hooks rule, and Svelte has no such rule. No is prefix either — predicates are bare words read as implicit questions: mounted(), idle(), documentVisible(). The question lives in the library, the answer in your variable: const isMounted = mounted().

That's why the name. Kenspeckle things are known at sight; so is every export here. The word is the spec.

Attachments, curried

Element-bound utilities are one name in two forms. Options only → returns an attachment. Element first → the imperative helper, cleanup returned.

<script>
	// same import, imperative
	const cleanup = clickOutside(node, () => close());
</script>

<div {@attach clickOutside(() => close())}>…</div>

Value-producing attachments are readable boxes — the factory's return is the attachment, carrying reactive getters:

<script>
	import { elementSize } from 'kenspeckle';

	const size = elementSize(); // no target yet
</script>

<div {@attach size}>…</div><p>{size.width} × {size.height}</p>

The same export works imperatively with a getter: elementSize(() => node). Argument presence picks the mode.

Install

npm install kenspeckle

Peer dependencies: svelte >= 5.40, plus @sveltejs/kit >= 2 for the kenspeckle/kit subpath (optional). SSR-safe — value factories return inert defaults on the server.

What's shipped

Five exports, two entry points. kenspeckle is Svelte-only; kenspeckle/kit adds the SvelteKit pieces, so $app/navigation never enters the main entry.

| export | form | lineage | | ----------------------- | -------------------------------------- | -------------------------- | | FiniteStateMachine | class, with typed reactive context | runed FiniteStateMachine | | copy() / copyText() | attachment + helper | svelte-put copy | | viewTransition() | function; navigation form under /kit | new | | viewTransitionName() | attachment + helper, /kit | new | | retreat() | function, returns a disposer, /kit | new |

FiniteStateMachine gains a typed, $state-backed context object visible to lifecycle hooks and guards — the sidecar-data mechanism every real FSM grows, built in.

View transitions are what the package is for so far; they get their own section below.

Planned

The rest of the curation. These names are the design, not an API you can call — nothing below has shipped.

| export | form | lineage | | ------------------------------------------------------------ | ------------------------------------------ | -------------------------------------------------------- | | StateHistory | class | runed StateHistory | | mounted() | value factory | runed IsMounted | | idle() | value factory | runed IsIdle | | documentVisible() | value factory | runed IsDocumentVisible | | focusWithin() | attachment, readable box | runed IsFocusWithin | | inViewport() | attachment, readable box | runed IsInViewport | | elementSize() | attachment, readable box | runed ElementSize | | elementRect() | attachment, readable box | runed ElementRect | | scrollState() | attachment, readable box; window form | runed ScrollState | | autosize() | attachment | runed TextareaAutosize | | clickOutside() | attachment + helper | runed onClickOutside · svelte-put clickoutside | | intersected() | attachment + helper | runed useIntersectionObserver · svelte-put intersect | | resized() | attachment + helper | runed useResizeObserver · svelte-put resize | | mutated() | attachment + helper | runed useMutationObserver | | shortcut() | attachment + helper | svelte-put shortcut | | lockScroll | attachment ≡ helper, stacked lock counting | new | | dragScroll() | attachment | svelte-put dragscroll | | debounced() / throttled() | value factory | runed Debounced / Throttled | | debounce() / throttle() | function wrapper | runed useDebounce / useThrottle | | previous() | value factory | runed Previous | | activeElement() | value factory | runed ActiveElement | | pressedKeys() | value factory | runed PressedKeys | | persisted() | value factory | runed PersistedState | | animationFrames() | value factory | runed AnimationFrames | | geolocation() | value factory | runed useGeolocation | | listen() | wiring | runed useEventListener | | interval() | wiring | runed useInterval | | searchParams() + helpers | value factory | runed useSearchParams | | watch / watchOnce / extract / onCleanup / boolAttr | unchanged | runed |

Dropped, deliberately: runed's Context — Svelte ≥ 5.40's createContext returns a typed [get, set] pair and covers it. runed's resource waits in the backlog — SvelteKit remote functions cover the server-data case; it returns only if a real client-only async need shows up.

View transitions

Two entry points. kenspeckle exports the same-route form; kenspeckle/kit adds the SvelteKit navigation form, so $app/navigation never enters the main entry. The /kit subpath expects @sveltejs/kit — an optional peer.

The contract

One attribute on <html>, absent when idle:

| data-view-transition | written by | | ---------------------- | ----------------------------------------------------- | | forward | a navigation | | retreat | a navigation the predicate claims, or a Back popstate | | step-forward | viewTransition(update) | | step-retreat | viewTransition(update, { retreat: true }) |

Every animation is yours, keyed off that value. An unprefixed ::view-transition-old|new(name) rule matches both kinds, so scope it when a step and a navigation must differ.

html[data-view-transition='forward']::view-transition-old(root) {
	animation: slide-out-left 300ms;
}

Navigations

Register once, from the root layout:

<script>
	import { viewTransition } from 'kenspeckle/kit';

	viewTransition();
</script>

Direction is a predicate, never a flag: Kit skips beforeNavigate for a navigation begun while another is in flight, so a flag set at click time can be stranded and reverse a later navigation. retreat() returns an identity-guarded disposer, shaped for $effect cleanup.

<script>
	import { retreat } from 'kenspeckle/kit';

	$effect(() => retreat((navigation) => navigation.to?.route.id === '/'));
</script>

A Back popstate is a retreat without asking. A Forward popstate is not.

Steps within a route

onNavigate never fires for a wizard advancing its own state, so drive the primitive directly:

<script>
	import { viewTransition } from 'kenspeckle';

	let step = $state(0);
	const go = (delta) => viewTransition(() => (step += delta), { retreat: delta < 0 });
</script>

The update callback suspends rendering until it settles, which past half a second reads as a hang — deadline (600ms) skips the transition rather than let it. With no document.startViewTransition, or under prefers-reduced-motion, the update still runs, untransitioned, and no attribute is written.

Named elements

viewTransitionName claims view-transition-name for the duration of a transition and releases it after. Two elements holding one name silently abort the whole transition, which is why claims are scoped rather than left in static CSS.

<button {@attach viewTransitionName('back-button')}>Back</button>

when claims only for navigations it accepts; onArrival claims inside the update callback instead, for an element that mounts during the transition. The imperative form takes the element first and returns a disposer:

const dispose = viewTransitionName(element, 'back-button');

Claims are navigation-only. The registration form drives the capture and arrival phases; viewTransition(update) on a same-route step names nothing. Morph a stepping element with static view-transition-name in CSS instead.

Claude Code plugin

This repo doubles as a Claude Code plugin. skills/view-transitions/ is the agent-facing guide to the view-transition tier — the data-view-transition contract, viewTransitionName claims and their phases, the duplicate-name abort, and the list of things the library already handles so an agent stops reimplementing them. Point a marketplace entry at this repo (.claude-plugin/plugin.json is at the root) and it installs as kenspeckle, invoked as /kenspeckle:view-transitions.

The skill and the code ship in the same change, same rule as the docs pages.

Provenance

kenspeckle is a curation, not a fork. runed has genuinely useful utilities under three naming regimes at once — PascalCase classes, bare functions, and React-style use hooks, with pairs like Debounced / useDebounce coexisting. svelte-put has good actions from the Svelte 4 era, before attachments existed. kenspeckle reshapes both pools under one convention and adds what neither ships: attachments as a first-class layer.

Portions adapted from runed (MIT) and svelte-put (MIT). kenspeckle is MIT.