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

deskplane

v0.1.3

Published

A framework-agnostic, two-dimensional virtual desktop navigator for the web.

Readme

Deskplane

A framework-agnostic, two-dimensional virtual desktop navigator for the web.

The package owns coordinates, active state, gestures, and movement. Your application owns all content, controls, indicators, and visual design.

deskplane-preview.png

Demo: Codepen

The package is available on npm.

Features

  • One to three independently positioned rows.
  • Any number of desktops within each row.
  • Configurable initial desktop, defaulting to the center desktop of the center row.
  • Horizontal and vertical movement without diagonal transitions.
  • Invisible destination-row pre-alignment before vertical navigation.
  • Explicit horizontal, vertical, or two-axis swipe zones.
  • Gesture thresholds, flick velocity, transition duration, and easing options.
  • External controls with active-state subscriptions.
  • Responsive desktop sizing from any mounted viewport element.
  • Container-query-ready desktop roots.
  • DOM and accessibility state restoration on teardown.
  • No runtime dependencies, theme, icons, fonts, or other assets.

Installation

After publication:

npm install deskplane

Import the controller and the structural stylesheet:

import { createDeskplane } from "deskplane";
import "deskplane/style.css";

Basic usage

const deskplane = createDeskplane({
  viewport: document.querySelector("#viewport"),
  rows: [
    {
      id: "top",
      desktops: [
        { id: "overview", element: document.querySelector("#overview") },
        { id: "reports", element: document.querySelector("#reports") },
      ],
    },
    {
      id: "middle",
      desktops: [
        { id: "library", element: document.querySelector("#library") },
        { id: "home", element: document.querySelector("#home") },
        { id: "settings", element: document.querySelector("#settings") },
      ],
    },
    {
      id: "bottom",
      desktops: [
        { id: "contact", element: document.querySelector("#contact") },
      ],
    },
  ],
  initialDesktopId: "home",
  transition: {
    duration: 320,
    easing: "cubic-bezier(0.22, 1, 0.36, 1)",
  },
});

await deskplane.goTo("settings");
await deskplane.move("up");

The example omits application-specific null checks for readability. TypeScript users should resolve and validate their elements before passing them to the package.

The supplied desktop elements are moved into the viewport while the controller is active. destroy() restores their original DOM positions, relevant attributes, and inert state.

React adapter

React applications can use the optional deskplane/react entry point:

import { useState } from "react";
import { DeskplaneViewport } from "deskplane/react";
import type { Deskplane } from "deskplane";
import "deskplane/style.css";

export function ApplicationDesktops() {
  const [deskplane, setDeskplane] = useState<Deskplane>();

  return (
    <>
      <button onClick={() => void deskplane?.goTo("controls")}>Controls</button>
      <button onClick={() => void deskplane?.goTo("information")}>Info</button>

      <DeskplaneViewport
        className="application-viewport"
        initialDesktopId="information"
        onReady={(controller) => {
          setDeskplane(controller);
          return () => setDeskplane(undefined);
        }}
        rows={[
          {
            id: "main",
            desktops: [
              { id: "controls", children: <Controls /> },
              { id: "information", children: <Information /> },
              { id: "copilot", children: <Copilot /> },
            ],
          },
        ]}
      />
    </>
  );
}

The adapter has no application or routing assumptions. It creates the desktop containers and renders application content into them through React portals. React retains ownership of every component tree while the framework-agnostic Deskplane core owns positioning, gestures, and navigation state.

React and React DOM are optional peer dependencies: applications using only the core package do not need either framework.

External controls

Controls can live anywhere in the document. A control only needs a target desktop id:

const button = document.querySelector("#settings-button");

button.addEventListener("click", () => {
  void deskplane.goTo("settings");
});

const unsubscribe = deskplane.subscribe((snapshot) => {
  const active = snapshot.activeDesktopId === "settings";
  button.setAttribute("aria-pressed", String(active));
});

Subscriptions receive the current snapshot immediately and again when navigation state changes. They are independent of any UI framework.

Swipe zones

Gestures begin only on explicit swipe zones, preventing competition with forms, buttons, and scrollable application content.

Declarative zones are discovered within the viewport when the controller is created:

<div data-deskplane-swipe-zone="horizontal">Drag left or right</div>
<div data-deskplane-swipe-zone="vertical">Drag up or down</div>
<div data-deskplane-swipe-zone>Drag in either direction</div>

Zones can instead be supplied explicitly:

createDeskplane({
  viewport,
  rows,
  gestures: {
    zones: [
      { element: horizontalHandle, axes: "horizontal" },
      { element: verticalHandle, axes: "vertical" },
    ],
    lockThreshold: 8,
    distanceThreshold: 0.18,
    velocityThreshold: 0.5,
  },
});

The structural stylesheet applies touch-action: none only to swipe zones. Normal desktop content therefore keeps its native touch, scrolling, selection, and form behavior. Common interactive controls and [data-deskplane-no-swipe] are also ignored if nested inside a zone.

Container queries

Every mounted .deskplane-desktop is a size container. Application content can react to the actual viewport dimensions:

.account-desktop {
  container-name: account;
}

@container account (min-width: 42rem) {
  .account-layout {
    grid-template-columns: 2fr 1fr;
  }
}

API summary

createDeskplane(options) returns:

  • snapshot — active row, active desktop, each row's remembered desktop, and animation state.
  • goTo(desktopId) — navigate directly to an associated desktop.
  • move(direction) — move up, right, down, or left; resolves to false at an edge.
  • isActive(desktopId) — check whether a desktop is active.
  • subscribe(listener) — observe state and receive an unsubscribe function.
  • destroy() — remove package DOM and listeners and restore supplied elements.

Invalid layouts, duplicate ids, duplicate elements, unknown desktop ids, and more than three rows fail early with descriptive errors.

Demo

Run the one included demonstration:

npm run dev

It demonstrates a non-fullscreen viewport, unequal row lengths, fixed external controls, active-state reflection, swipe rails, container queries, normal buttons, forms, and scrollable content.

Development and quality checks

Install dependencies and run the complete quality gate:

npm install
npm run check

npm run check verifies Prettier formatting, type-aware ESLint rules, strict TypeScript types, Vitest tests, the distributable package build, and the production demo build.

Pure navigation and state transitions are covered by fast unit tests. DOM mounting, restoration, active state, row pre-alignment, and pointer gestures are tested in a browser-like DOM. Real-browser visual checks cover responsive rendering and container behavior.

Other commands:

npm run format
npm run lint
npm run typecheck
npm test
npm run test:watch
npm run build
npm run build:demo

License

MIT