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

nodality

v1.3.24

Published

A lightweight library for declarative UI elements.

Readme


A whole navigation graph is one node

{ op: "morph", effect: "t-vhs", duration: 620, back: true,
  chain: [
    { from: "home",   to: { Work: "work", Contact: "contact" } },
    { from: "work",   to: { Aurora: "aurora" },  effect: "t-split" },
    { from: "aurora", to: { Contact: "contact" }, effect: "t-bloom" },
  ] }

That is the entire source of the transitions above. The entries are edges, not keyframes – edge two is reachable from the state edge one lands on, so a landed view becomes a source in its turn. Settings on the node are defaults each edge may override, and back unwinds the path the user actually took rather than looking up an edge.

Effects run on live DOM, and the DOM stays clickable

const nodes = [
  { op: "flow",   target: ["bar"], by: "mouse", amount: 0.32 },
  { op: "dither", target: ["bar"], levels: 10, size: 2, amount: 0.14 },
];

Two raster nodes aimed at one element compose into a single shader pass, in array order. The source is your live DOM, not an image you exported – and the links underneath still work: pointer input is inverted through the compiled transformation and re-dispatched, so a click lands where the user aimed rather than where the layout still thinks the link is. In the recording above, the pointer drives the warp and then clicks through it; the view that follows is the morph firing.

Install

npm create nodality@latest my-app

Or drop it into a page – no build step, no dependencies:

<div id="mount"></div>

<script type="module">
import { Des } from "https://unpkg.com/nodality/dist/index.esm.js";

const elements = [
  { id: "hero", type: "h1", text: "Hello" },
];

const nodes = [
  { op: { name: "gradient", gradient: "linear-gradient(90deg, #1d6fe0, #7fd1ff)" },
    target: ["hero"] },
  { op: "dither", target: ["hero"], levels: 6, size: 2 },
];

new Des().nodes(nodes).add(elements).set({ mount: "#mount" });
</script>

elements says what exists. nodes says what is done to it. Nothing in elements names an effect and nothing in nodes names a component – the two meet only through target, so either can be written, stored or generated without the other.

What else it does

Static site generation. The same pair is interpreted at build time in a simulated DOM, so npx nodality prerender emits crawlable HTML that a consumer running no JavaScript can read, plus a sitemap, hreflang alternates and JSON-LD. The browser then re-reads the same pair and rebuilds on top – the pair is shipped, not consumed. Docs →

Transitions and navigation graphs. { op: "morph" } in one hop or a whole chain, with per-edge effects, history-based back, and two capture backends chosen automatically. Docs →

Composable GPU effects. Fifteen first-party raster ops, driveable by pointer, scroll or time, composed into one pass. Docs →

An agent-operable surface, derived. One node exposes your page to an AI agent as callable tools – traversing views, submitting a form you allowed, reading what is on screen – derived from the graph you already wrote rather than authored a second time, and also written into the prerendered HTML as a static manifest. Docs →

For AI agents

One command sets up an agent IDE end to end:

npx nodality skill

It installs the /nodality skill – the authoring workflow, the node families, and the house rules the schema cannot encode – into Claude Code (.claude/skills/) and Cursor (.cursor/rules/), and registers the MCP server below in the project's config. Flags: --claude, --cursor, --global, --dir=<path> for any other agent, --no-mcp. Re-run it after upgrading nodality to refresh the skill in place.

nodality mcp runs a Model Context Protocol server over stdio, so a generator writes against the real vocabulary instead of guessing at it:

{ "mcpServers": { "nodality": { "command": "npx", "args": ["nodality", "mcp"] } } }

Three tools. list_ops returns the op registry as data – every op with its stage, parameters, units and defaults. validate_nodes checks a node array and returns a machine-readable report with did-you-mean suggestions, so { op: "dithr" } comes back as UNKNOWN_OP suggesting dither rather than rendering nothing at all. preview renders an (E, N) pair to a self-contained HTML file (needs jsdom in your project; it is required only for prerendering, so the library does not bundle it).

The validator is available directly too:

import { validateNodes, describeOps } from "nodality/validate";

What this is not

Nodality targets static, content-shaped sites – marketing pages, storefronts, documentation, brochure sites. It is not a replacement for a component framework with client-side state management, and it does not try to be one.

Two honest caveats. The HTML-in-Canvas capture backend is experimental and flag-gated: where it is unavailable the pipeline falls back to snapshot capture on its own, so a page authored against it degrades to stills rather than breaking. And determinism is a property of the compiled artefact – the same pair always produces the same output – not of the pixels a particular GPU eventually paints.

Requirements

Nodality has zero runtime dependencies. What you need depends on how you use it.

The preferred way to start is the scaffolder, which creates a working project with the pipeline, the prerender step and the example pair already wired up:

npm create nodality@latest my-app

It needs only Node.js 20 or newer. Everything below is for adding Nodality to something that already exists.

| Use | Requires | |---|---| | In a browser | Any browser with ES modules. No build step, no bundler. | | npm install nodality | Node.js 20 or newer. TypeScript declarations ship in dist/index.d.ts. | | npx nodality prerender (static generation) | Node.js 20 or newer. The prerender path runs the same pipeline inside jsdom. | | Running the test suite | npm install (pulls Playwright, jsdom, webpack, Babel as dev dependencies), then npx playwright install. |

The published measurements were produced on Node.js v25.6.0.

Code information

| Path | Contains | |---|---| | layout/ | Element definitions — the what exists half of the pair. | | lib/ | Transformation nodes and the pipeline — the what is done to it half. | | dist/ | Built bundles: index.cjs.js (CommonJS), index.esm.js (ES module), index.d.ts. | | bin/ | The command-line entry points, including prerender. | | __tests__/ | Playwright regression tests that assert deterministic output. | | examples/ | Runnable pairs, smallest first. | | API.md | Full API reference. | | CHANGELOG.md | Version history. |

Reproducing the published measurements

The quantitative results are not in this repository. Harnesses, raw data and per-measurement READMEs are deposited separately under CC0:

That deposit covers authoring cost in language-model tokens, compiler scaling from 10 to 1 000 elements, crawlability and first-paint timing, model properties, determinism, accessibility, and WHATWG validity. Each has its own README and run command. The benchmark harnesses pin nodality to an exact version rather than a range, so a fresh install reproduces the published figures against the artefact that produced them.

Methodology

Nodality compiles two plain arrays — elements (what exists) and nodes (what is done to it) — into rendered output. The two meet only through target, so neither names the other and either can be authored, stored or generated independently. The same pair is interpreted twice: at build time inside a simulated DOM to emit crawlable static HTML, and in the browser to attach behaviour. Determinism is a property of the compiled artefact: the same pair always compiles to a byte-identical result, which is asserted by the regression suite rather than proved formally.

Citing

If you use Nodality in research, please cite the paper rather than this repository:

Vabroušek, F. Nodality: a node-based declarative transformation pipeline for static UI generation. PeerJ Computer Science. (Under review.)

For the measurements specifically, cite the data deposit:

Vabroušek, F. 2026. Nodality benchmarks. Zenodo. DOI 10.5281/zenodo.21610251.

License and contributing

Nodality is released under the MIT License. The benchmark deposit is released separately under CC0 1.0.

Contributions are welcome; see CONTRIBUTING.md for the development setup and the expectations for a pull request.

Links

MIT © Filip Vabroušek