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

@boccdotdev/data-reveal

v0.3.1

Published

Declarative, scroll-triggered reveal animations driven by data attributes. GSAP-powered, code-split so each animation type loads only when present on the page.

Readme

@boccdotdev/data-reveal

Declarative, scroll-triggered reveal animations driven by HTML data- attributes. Powered by GSAP + ScrollTrigger.

  • Code-split — each animation type loads on demand. A page that only uses fade never downloads the split code (or its SplitText plugin).
  • Zero side effects on import — nothing runs, and no GSAP is loaded, until you call init().
  • Respects prefers-reduced-motion — elements snap to their final state instead of animating.

Install

npm install @boccdotdev/data-reveal gsap

gsap is a peer dependency (>=3.13.0) — you install it yourself so the package shares your single GSAP instance. GSAP 3.13+ ships ScrollTrigger and SplitText for free; no Club membership required.

Quick start

import { createReveal } from "@boccdotdev/data-reveal";

const reveal = createReveal();
await reveal.init();          // scan the document, load only the types in use
<h2 data-reveal="split" data-reveal-split="words">Animated headline</h2>
<div data-reveal="fade" data-reveal-direction="left">Fades in from the left</div>
<span data-reveal="counter" data-reveal-to="1200" data-reveal-separator=",">0</span>

That's it. Add a data-reveal attribute to any element and it animates as it scrolls into view.

How loading works

init() scans the container for [data-reveal], collects the set of types present, then dynamically import()s only those modules. Your bundler (Vite, webpack, etc.) splits each type into its own chunk, so the browser fetches code for an animation only when a page actually uses it. GSAP itself rides along in those chunks — a page with no reveals loads no GSAP.

Because loading is dynamic, init() is async. await it (or .then()) if you need to run code after reveals are set up.

Minimal builds (manual registration)

Auto mode is convenient, but to dynamically load any type on demand it has to reference every type — so your bundler emits a chunk file for all of them, even ones your site never uses. They're never downloaded unless present on a page, but the files still sit in your build output.

If you want only the types you use to exist in your build at all, import the /manual entry and register types explicitly. It has no reference to the auto-loader, so unused type modules (and their GSAP plugins) are never bundled:

import { createReveal } from "@boccdotdev/data-reveal/manual";
import { fade } from "@boccdotdev/data-reveal/fade";
import { scale } from "@boccdotdev/data-reveal/scale";

const reveal = createReveal({ types: [fade, scale] });
await reveal.init();

Each type is importable from its own subpath: @boccdotdev/data-reveal/{fade,scale,split,batch,blur,clip,rotate,slide,counter,parallax}. The HTML API is identical — data-reveal="fade" still works; you've just told the bundler which types are in play. A data-reveal type you didn't register is silently skipped.

The returned instance is the same as auto mode — init, refresh, destroy all behave identically. Choose auto for zero-config convenience, manual when build size matters.

API

const reveal = createReveal();

await reveal.init();              // scan document
await reveal.init(myContainer);   // scan a subtree only (any ParentNode)

reveal.refresh();                 // recompute all ScrollTrigger positions

reveal.destroy();                 // kill everything, revert SplitText, forget all elements
reveal.destroy(myContainer);      // kill + forget only reveals on/inside myContainer

init() is idempotent — already-initialized elements are skipped, so you can safely call it again as new content is added (e.g. infinite scroll).

refresh()

Recomputes every ScrollTrigger's start/end positions. ScrollTrigger already refreshes on window resize and load, but a JavaScript-driven layout change doesn't fire that — so call refresh() after toggling a grid's column count, opening an accordion, or when fonts/images finish loading and shift the page. Calls are coalesced to one refresh per frame, so it's cheap to call repeatedly.

destroy(container?)

With no argument, kills all tweens + ScrollTriggers, reverts SplitText, and forgets every element — a later init() re-runs the whole page. Pass a container to tear down only the reveals on or inside it; those elements are forgotten too, so re-adding content there and calling init() re-animates just that subtree. contains works on detached nodes, so you can pass a subtree you're about to remove. Use the scoped form for SPA partial swaps, the bare form for a full route change.

Animation types

All types accept these shared attributes:

| Attribute | Default | Notes | |-----------|---------|-------| | data-reveal-duration | varies | Seconds | | data-reveal-delay | 0 | Seconds | | data-reveal-start | top 85% | ScrollTrigger start position | | data-reveal-reverse | off | Add it to play the animation out again when scrolling back up past start. Default is play-once. (parallax is always bidirectional and ignores this.) |

fade

Fade + translate in.

| Attribute | Default | Values | |-----------|---------|--------| | data-reveal-direction | up | up down left right | | data-reveal-distance | 20 | px |

scale

Fade + scale up.

| Attribute | Default | Notes | |-----------|---------|-------| | data-reveal-from | 0.92 | Starting scale |

split

Per-character/word/line text reveal (uses SplitText).

| Attribute | Default | Values | |-----------|---------|--------| | data-reveal-split | chars | chars words lines | | data-reveal-stagger | 0.02 | Seconds between units |

batch

Staggered reveal of many children as they enter together. Targets elements marked data-reveal-item, or falls back to direct children.

| Attribute | Default | Notes | |-----------|---------|-------| | data-reveal-stagger | 0.06 | Seconds | | data-reveal-distance | 20 | px |

<ul data-reveal="batch">
  <li data-reveal-item>One</li>
  <li data-reveal-item>Two</li>
</ul>

blur

Fade in from a blur.

| Attribute | Default | Notes | |-----------|---------|-------| | data-reveal-blur | 12 | Starting blur in px |

clip

clip-path wipe reveal.

| Attribute | Default | Values | |-----------|---------|--------| | data-reveal-direction | up | up down left right (wipe direction) |

rotate

3D rotate in.

| Attribute | Default | Values | |-----------|---------|--------| | data-reveal-axis | x | x y z | | data-reveal-rotate | 90 | Degrees |

slide

Slide in from fully off the element's own box.

| Attribute | Default | Values | |-----------|---------|--------| | data-reveal-direction | up | up down left right |

counter

Count a number up as it enters view. Reads the target from data-reveal-to, or the element's text content.

| Attribute | Default | Notes | |-----------|---------|-------| | data-reveal-to | element text | Target number | | data-reveal-from | 0 | Starting number | | data-reveal-decimals | 0 | Decimal places | | data-reveal-separator | — | Thousands separator, e.g. , | | data-reveal-prefix | — | e.g. $ | | data-reveal-suffix | — | e.g. % |

parallax

Continuous, scroll-linked motion (scrubbed — not a one-shot reveal). Disabled under reduced motion.

| Attribute | Default | Notes | |-----------|---------|-------| | data-reveal-speed | 0.3 | Higher = more movement | | data-reveal-start | top bottom | ScrollTrigger start | | data-reveal-end | bottom top | ScrollTrigger end |

License

MIT