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

moviola

v0.1.0

Published

The scrollytelling framework — you write the DOM, moviola runs the state machine, effects live in your CSS.

Readme

moviola

CI

The scrollytelling framework. You write the DOM, moviola runs the state machine, effects live in your CSS.

Drop in one script tag and one stylesheet, mark up your story as sections, and moviola turns scroll position into classes, attributes, and CSS custom properties. It never sets a visual property itself — your CSS does every effect. 4.5 KB gzipped, zero dependencies, no build step, runs from file://.

Status: pre-1.0 (v0.1.0), not yet published to npm. The contract below is stable and covered by tests. Until the first publish, install by vendoring the two files (see Install).

Demo

Four scroll positions of examples/virus-got-out.html: the camera starts tight on Wuhan, pulls back through the rail network and the cordon, and ends on the whole world with flight arcs converging

One story, four scroll positions — the camera pulling back as the reader scrolls. Nothing in it is scripted: the camera moves because each step carries a data-focus, the cards and colours change because CSS reacts to [data-active-step], and the only JavaScript on the page is Moviola.init().

Every example is a single self-contained HTML file you can open straight from disk — no server, no build:

| | | |---|---| | index.html | the tour: layouts, crossfades, progress, themes | | examples/virus-got-out.html | camera flights over a map, scrubbed particle flows — zero author JS | | examples/zoom-tour.html | a nine-shot zoom tour of a decision tree, camera and scrub in the same chapters |

Full list in the Gallery.

Install

Vendor the files. Copy dist/moviola.min.js and dist/moviola.css next to your page and reference them directly. This is the intended path: the library is small enough to commit, and pages stay openable from file://.

<link rel="stylesheet" href="moviola.css">
<script src="moviola.min.js"></script>

Or clone and build. dist/ is committed, so this is only needed if you change src/:

git clone https://github.com/sixthshift/moviola.git
cd moviola && bun install && bun run build

See CONTRIBUTING.md.

npm — the package is moviola, not published yet (tracked in Roadmap). The manifest is ready, so this works unchanged on the day it ships:

import Moviola from 'moviola'   // dist/moviola.esm.js, types included

A moviola is the editing bench where a cutter holds the film and scrubs it back and forth by hand. That is what this library makes of a web page: no clock, no playback, just a reel and a reader's thumb on it.

What's in dist/:

| File | | |---|---| | moviola.min.js | classic <script>, sets window.Moviola — the canonical artifact | | moviola.css | structural stylesheet (pinning, layouts, mobile collapse) | | moviola.esm.js | ES module, no global side effect | | moviola.d.ts | TypeScript types |

Quick start

<link rel="stylesheet" href="moviola.css">

<article class="moviola" data-layout="side-right">
  <figure>
    <img src="map.png" data-show="intro">
    <img src="map-2008.png" data-show="crash recovery">
  </figure>
  <section class="step" id="intro">…prose…</section>
  <section class="step" id="crash">…prose…</section>
  <section class="step" id="recovery">…prose…</section>
</article>

<script src="moviola.min.js"></script>
<script>Moviola.init()</script>

The <figure> pins to the viewport; the steps scroll past it. As each step crosses the trigger line, moviola updates the state below and your CSS does the rest.

The contract

Everything moviola does is expressed as state your CSS can react to:

| Surface | What it carries | |---|---| | .step classes | is-past / is-active / is-future | | .moviola[data-active-step="id"] | lets any selector on the page react to any step | | [data-show="id …"] graphic children | is-shown while a listed step is active (crossfade by default) | | --step-progress / --story-progress | 0 → 1 through the active chapter / the whole story | | --progress-<id> | per-chapter progress that never resets — the scrub timeline | | --camera-transform | the current camera shot, interpolated between focused steps |

That is the whole mental model. The full reference — every attribute, the data-show range syntax, the declarative motion layer (data-scrub/data-camera/data-focus/data-morph), and the JS API for imperative graphics — is in docs/api.md.

Referential mistakes never break the page: a token matching nothing fails soft and logs one moviola:-prefixed warning.

Recipes

The full set lives in docs/recipes.md. These two are whole stories rather than fragments — paste either into a page that already loads moviola.css and moviola.min.js and it runs — and each ships as a live fixture (e2e/fixtures-clean/recipe-*.html) the e2e suite scrolls end to end, so a recipe that stops working cannot stay published.

A photo as the camera stage

A raster gives a selector nothing to point at, which is what raw-coordinate data-focus is for: each shot names a box of the image itself.

<article class="moviola" data-layout="side-right">
  <figure>
    <svg viewBox="0 0 320 200" width="100%" height="100%">
      <g data-camera>
        <image width="320" height="200" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAUCAMAAADbT899AAAAElBMVEUbT3I2faNOejptnE7PybizRS+ryHFxAAAAYUlEQVR42sWOSQ7AMAgDATv//3LVKguoFuqtXBwxI2KzP4eflr5fYwjf/S14nTjHmTAXjirMXZksCFyEhQFgZhKcrHzFbk1KvoUgA4LDTh3JjwDNw+THKQw9fy50/O7QclzLewORR14f8wAAAABJRU5ErkJggg=="/>
      </g>
    </svg>
  </figure>
  <section class="step" id="coast" data-focus="0 0 320 200" data-shot="wide"><p>The whole coast.</p></section>
  <section class="step" id="jetty" data-focus="60 40 80 110" data-shot="medium"><p>Down to the jetty.</p></section>
  <section class="step" id="hut" data-focus="70 40 40 40" data-shot="close"><p>The hut at its end.</p></section>
</article>

That data: URI is a 32×20 stand-in — swap in your own photo.jpg. The stage has to be SVG: the camera composes its transform in the graphic's own coordinate space, and a bare <img> has none. The boxes are read in that same space, so 0 0 320 200 is the whole viewBox.

Scrubbing one chapter

<style>
  .dial { width: 300px; height: 40px; background: #dde3ee }
  .needle { width: 6px; height: 40px; background: crimson }
  .needle[data-scrub] { animation-name: sweep }
  @keyframes sweep { from { transform: translateX(0) } to { transform: translateX(294px) } }
</style>
<article class="moviola" data-layout="side-right">
  <figure>
    <div class="dial"><div class="needle" data-scrub="rising"></div></div>
  </figure>
  <section class="step" id="calm"><p>The needle waits at 0%.</p></section>
  <section class="step" id="rising"><p>Through this chapter it tracks your scroll.</p></section>
  <section class="step" id="after"><p>Arrived — and it rewinds if you scroll back up.</p></section>
</article>

You own animation-name and nothing else: moviola stamps --t from var(--progress-rising) and normalizes the duration, so the @keyframes are the whole timeline. Drop the value — plain data-scrub — to scrub against --story-progress instead of one chapter.

Gallery

Each example is a self-contained file://-openable page. "Zero author JS" means the only script on the page is Moviola.init() — everything moving is CSS reacting to moviola's state, verified by scripts/validate-story.mjs --tier1, which drives every chapter forward, in reverse, and out of order and checks that a chapter frames the same graphic whichever way the reader reached it.

| Example | What it demonstrates | |---|---| | warming-world.html | Stepped line-chart build: six warming factors added one at a time. Zero author JS. | | stepped-scatter.html | Sticky scatter with stepped cohort highlighting and long prose gaps between steps. Zero author JS. | | scroll-linked.html | A classifier's boundary rotating and its points drifting — pure @keyframes scrubbed by data-scrub. Zero author JS. | | virus-got-out.html | Camera flights, offset-path particle flows, a scroll-linked bloom, chapter theming. Zero author JS. | | zoom-tour.html | Nine camera flights across one large stage while scrubbed elements move in the same chapters. Zero author JS. | | dots-flow.html | Particles regrouping per step, each travelling via data-morph + view-transition-name. Uses stepenter. | | longform.html | Three independent stories on one page, mixing all three layouts. |

Themes

Typography and color presets, opt-in, loaded after moviola.css:

<link rel="stylesheet" href="moviola.css">
<link rel="stylesheet" href="themes/editorial.css">

Themes only set typography, color, and the --moviola-card-bg / --moviola-card-fg knobs — never geometry. Write your own if neither fits; the contract is the classes and custom properties, not these files.

Browser support

Any browser with IntersectionObserver and CSS custom properties — every current release of Chrome, Safari, Firefox, and Edge. Two things degrade rather than break:

  • data-morph needs the View Transitions API. Without it the step change still happens, just without the transition.
  • prefers-reduced-motion: reduce turns every scrub and camera flight into a cut at the chapter midpoint, and skips morphs entirely.

A page whose JavaScript never runs stays fully readable — all hiding CSS is scoped under .moviola.is-ready, which the runtime stamps last.

Keyboard: / step between chapters while a story is on screen (smooth, reduced-motion aware). Vertical scroll keys are never touched.

Authoring tools

Neither costs the library a byte:

  • moviola-director.js — the viewfinder. Load it like a theme with one extra script tag while you author, press d, and it draws the trigger line, a clickable chapter rail, and a live chip of the state your CSS is reacting to.
  • node scripts/validate-story.mjs page.html --report out.html — drives your story forward, in reverse, and out of order in a real browser, then writes a storyboard: one row per chapter, the forward frame beside the reverse frame, so every continuity verdict comes with the two pictures behind it.

Docs

| | | |---|---| | docs/api.md | full reference — every attribute, variable, and event | | docs/recipes.md | "does moviola do X?" — progress bars, maps, video scrub, D3 builds | | docs/philosophy.md | why it's shaped this way, and what it deliberately isn't | | ARCHITECTURE.md | how the repo implements it | | SPEC.md | the normative contract | | CONTRIBUTING.md | setup, scripts, the rules that matter |

Roadmap

  • first npm publish as moviola, plus jsdelivr/unpkg
  • full layout variant

License

MIT © Jason Huang