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

react-scrim

v1.0.0

Published

A scrim store for React: declare the nodes and how they animate once, then cover and uncover from anywhere, route loaders included.

Readme

react-scrim

A scrim store for React. Declare the nodes and how they animate once, then cover and uncover from anywhere, route loaders included.

Package: npmjs.com/package/react-scrim

Documentation: dev.robertoattanasio.com/react-scrim

Install

npm install react-scrim

Usage

import { createScrim } from "react-scrim";

export const scrimRouteConfig = createScrim({
  nodes: ["curtain"],
  animation: { duration: 650, easing: "cubic-bezier(0.65, 0, 0.35, 1)" },
  hold: 1000,
  until: () => document.fonts.ready,
  enter: ({ curtain }, { status, play }) =>
    play(
      curtain,
      status === "idle"
        ? [{ transform: "translateY(100%)" }, { transform: "translateY(0)" }]
        : [{ transform: "translateY(0)" }],
    ),
  leave: ({ curtain }, { play }) => play(curtain, [{ transform: "translateY(-100%)" }]),
});
<div ref={scrimRouteConfig.node("curtain")} className="fixed inset-0" />
export const Route = createFileRoute("/about")({
  loader: () => scrimRouteConfig.cover(),
});
router.subscribe("onResolved", () => scrimRouteConfig.uncover());
import { useScrim } from "react-scrim";

const { status, isReady } = useScrim(scrimRouteConfig);

Timing

The store has no idea how long anything takes. cover and uncover don't carry a duration — they call enter/leave and wait for whatever those return, which in practice is play's return value: the same Animation.finished promise the browser resolves once the animation actually on screen has actually finished. There's nowhere else the time is written down, so there's nothing to keep in sync — no CSS duration to match against a JS timer, no transitionend that fires twice for two properties or not at all when an animation is replaced mid-flight.

leave can compose more than one play call — a curtain and a page, each with its own delay, duration and easing — and uncover still just awaits the result. It doesn't know either duration; it waits for whichever finishes last. The choreography is a consequence of what's composed, not a number anyone maintains.

hold is the one exception: a fixed pause the store keeps for itself, deliberately, between being ready and starting to leave.

Why this also means an interrupted navigation just works: dev.robertoattanasio.com/react-scrim for the idea, /react-scrim/create-scrim#section-interruptions for how.

Options

| option | what it does | | ----------- | --------------------------------------------------------------------------------------------- | | nodes | the names of the nodes the store animates | | initial | "covered" (default) starts covering, for a splash. "idle" starts uncovered | | animation | default options for every play call | | hold | milliseconds to stay covered before leaving | | until | what to wait for before the first leave | | enter | the enter animation. status is "idle" when starting from rest, "leaving" when reversing | | leave | the leave animation |

enter, leave and until can return anything that can be awaited, so GSAP and Motion work in place of play.

Store

| method | what it does | | -------------- | -------------------------------------------------- | | node(name) | a ref that registers a node | | cover() | runs enter. Resolves once covered | | uncover() | waits for until and hold, then runs leave | | subscribe | listens to every change | | getStatus() | "idle", "entering", "covered" or "leaving" | | getIsReady() | true once until has resolved |

License

MIT