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

motion-tokens

v0.3.6

Published

Token-based GSAP scroll animation system. Compose any animation with a simple string: fade-up, blur-left-slow-scrub, words-fast, chars-up.

Readme

motion-tokens

Declarative scroll animations for the modern web. Compose any GSAP animation with a single data-animate attribute — no per-element JavaScript, no configuration files, no boilerplate.

<div data-animate="fade-up">Fades in from below</div>
<div data-animate="blur-left-slow">Blurs in from the left, slowly</div>
<p data-animate="words-up-bounce">Word-by-word reveal with bounce easing</p>
<div data-animate="fade|blur-up-spring">Compound fade + blur with spring ease</div>
<div data-animate="clip-left">Clip-path reveal from left</div>

Why motion-tokens?

Most animation libraries force you to choose: write verbose JavaScript for every element, or accept a limited set of presets. motion-tokens gives you a composable token grammar that covers the gap. You get hundreds of animation combinations from a handful of memorizable tokens, all driven from your HTML.

  • Zero JavaScript per element — just add data-animate and you're done
  • Composable grammar — mix effects, directions, speeds, and eases freely
  • Automatic scroll triggering — powered by GSAP ScrollTrigger under the hood
  • Dynamic DOM support — MutationObserver detects elements added after init
  • Accessible by default — respects prefers-reduced-motion automatically
  • Tiny API surface — one function to call, one attribute to learn

Install

npm install motion-tokens gsap@"~3.13.0"

GSAP >=3.13.0 <3.14.0 is a required peer dependency. All GSAP plugins (including SplitText) became free in 3.13. Version 3.14+ introduces breaking SplitText changes and is not supported.

Quick Start

import { initAnimations } from 'motion-tokens';

initAnimations();
// Done. Every [data-animate] element now animates on scroll.

Token Grammar

Tokens follow a simple pattern:

data-animate="[effect](-[direction])(-[speed])(-[ease])(-[mode])(-[trigger])"

Only effect is required. Everything after it is optional and order-independentfade-up-slow-bounce and fade-bounce-slow-up both resolve identically.

Compound Effects

Combine multiple effects with the pipe (|) operator:

<div data-animate="fade|blur-up">Fade + blur from below</div>
<div data-animate="fade|scale-up-spring">Fade + scale with spring ease</div>

When two effects write to the same CSS property, the last one wins. If a compound includes a text/stagger effect, non-text effects are dropped.


Token Reference

Effects

| Token | What it does | |---------|------| | fade | Opacity fade with optional directional offset | | blur | Opacity fade + gaussian blur | | scale | Scale up from smaller + fade | | zoom | Scale up from center, no directional offset | | pop | Scale up with a punchy back.out ease | | slide | Slide in from off-screen | | flip | 3D rotation reveal | | clip | Clip-path reveal (direction sets the origin) | | stagger | Animate child elements sequentially | | words | Split text → reveal word by word | | lines | Split text → reveal line by line | | chars | Split text → reveal character by character | | counter | Animate a number from start → end | | draw | SVG stroke draw animation | | float | Gentle floating idle loop (not scroll-triggered) | | twist | 3D character reveal on scroll; use twist-hover for an interactive hover loop |

Directions

| Token | Meaning | |-------|---------| | up | From below | | down | From above | | left | From the left | | right | From the right |

Most effects support directions. clip uses direction to set the clip origin. float, counter, draw, and twist ignore direction.

Speeds

| Token | Duration multiplier | |-------|------| | xfast | 0.6× | | fast | 0.75× | | (default) | 1.0× | | slow | 1.5× | | xslow | 2.0× |

Easing

| Token | Resolves to | |-------|------------| | bounce | back.out(1.7) | | spring | elastic.out(1, 0.5) | | elastic | elastic.out(1.2, 0.3) | | expo | expo.out | | power | power3.out |

Playback Modes

| Token | Behavior | |-------|----------| | scrub | Locks animation to scroll position | | once | Plays once, then destroys the ScrollTrigger | | norepeat | Plays on enter, does not reverse on scroll-up |

Triggers

| Token | Behavior | |-------|----------| | (default) | Scroll-triggered via ScrollTrigger | | hover | Plays on mouseenter, reverses on mouseleave | | click | Toggles on each click | | focus | Plays on focus, reverses on blur |

Triggers can also be set separately via the data-trigger attribute.


Practical Examples

fade-up                → Fade in from below
fade-left-fast         → Fade in from left, 0.75× speed
blur-down-slow         → Blur in from above, 1.5× speed
scale-up-spring        → Scale + fade with elastic spring
flip-right-fast        → 3D flip from right, fast
words-up-bounce        → Word-by-word with bounce
fade-up-once           → Fade up, plays once only
clip-left              → Clip-path reveal from left
clip-down-slow         → Clip reveal from top, slowly
counter                → Animate number on scroll
draw                   → SVG stroke draw on scroll
float                  → Gentle floating idle loop
twist                  → 3D character reveal on scroll
twist-hover            → 3D character hover loop
fade-up-hover          → Fade in on hover
pop-click              → Pop toggle on click
scale-up-scrub         → Scale animation locked to scroll
fade|blur-up           → Compound: fade + blur from below
fade|scale-up-expo     → Compound: fade + scale with expo ease

Fine-Tuning with Data Attributes

Override any animation's defaults directly in HTML:

| Attribute | Type | Description | |-----------|------|-------------| | data-delay | number | Delay before animation starts (seconds) | | data-duration | number | Override duration (seconds) | | data-no-repeat | boolean | Play once, don't reverse on scroll-up | | data-scroll-range | string | Custom scroll range as viewport % ("start-end") | | data-stagger | number | Custom gap between stagger children (seconds) | | data-stagger-from | string | Origin for text reveals: start, end, center, edges, random | | data-trigger | string | Override trigger: scroll, hover, click, focus | | data-animate-out | string | Separate animation token for leaving the viewport | | data-sequence | string | Group name for sequenced animations | | data-sequence-order | number | Order within a sequence group | | data-counter-start | number | Starting value for counter | | data-counter-decimals | number | Decimal places for counter |

Usage Examples

<!-- Delayed, plays once -->
<div data-animate="fade-up" data-delay="0.3" data-no-repeat>
  Delayed entrance, no replay
</div>

<!-- Custom text timing -->
<p data-animate="words-up" data-duration="2" data-stagger="0.1">
  Slower word reveal with wider gaps
</p>

<!-- Separate enter/exit animations -->
<div data-animate="fade-up" data-animate-out="fade-down">
  Fades up on enter, fades down on leave
</div>

<!-- Sequenced group — elements animate in order -->
<h2 data-animate="fade-up" data-sequence="hero" data-sequence-order="1">Title</h2>
<p data-animate="fade-up" data-sequence="hero" data-sequence-order="2" data-delay="0.2">Subtitle</p>
<button data-animate="scale" data-sequence="hero" data-sequence-order="3" data-delay="0.4">CTA</button>

<!-- Counter -->
<span data-animate="counter" data-counter-start="0" data-counter-decimals="0">1500</span>

<!-- SVG draw -->
<path data-animate="draw" d="M10 80 C 40 10, 65 10, 95 80" />

<!-- Float (no scroll trigger) -->
<div data-animate="float">Gently bobs up and down</div>

<!-- Twist variants -->
<h2 data-animate="twist">3D character reveal on scroll</h2>
<h2 data-animate="twist-hover">3D character reveal on hover</h2>

<!-- Stagger-from control -->
<h1 data-animate="chars-up" data-stagger-from="random">Random character reveal</h1>
<p data-animate="words-up" data-stagger-from="center">Words from center outward</p>
<p data-animate="lines" data-stagger-from="end">Lines reveal bottom to top</p>

API

initAnimations(options?)

Initialize the system. Returns an AnimationSystem handle.

import { initAnimations } from 'motion-tokens';

const system = initAnimations({
  defaultDuration: 0.8,
  movement: 40,
  dev: true,

  // CDN / non-bundler usage: inject GSAP manually
  gsap: window.gsap,
  scrollTrigger: window.ScrollTrigger,

  // Register custom semantic names
  fallbackRegistry: {
    'hero-entrance': { effectImpl: 'fade', x: 0, y: 50, blur: 0 },
  },
});

AnimationSystem

| Member | Description | |--------|-------------| | destroy() | Kill all animations, remove MutationObserver, clean up | | refresh() | Re-scan the DOM for new data-animate elements | | engine | Access the underlying AnimationEngine | | reducedMotion | true if the user prefers reduced motion |

Configuration

All values below can be passed to initAnimations():

| Option | Default | Description | |--------|---------|-------------| | preset | — | Apply a named style preset as a baseline: "minimal", "editorial", "expressive", "playful" | | defaultDuration | 0.45 | Base duration (seconds) | | slideDuration | 0.7 | Duration for slide | | textDuration | 0.6 | Duration for text effects | | staggerDuration | 0.4 | Duration per stagger child | | movement | 24 | Directional offset (px) | | slideDistance | 50 | slide travel distance (px) | | blur | 3 | Blur amount (px) | | scaleStart | 0.92 | Starting scale for scale / zoom | | triggerPosition | "top 85%" | When ScrollTrigger fires | | scrubSmoothing | 1.2 | Scrub smoothing factor | | defaultStagger | 0.08 | Gap between stagger children (s) | | textStagger | 0.05 | Gap between text segments (s) | | clipDuration | 0.7 | Duration for clip | | counterDuration | 1.2 | Duration for counter | | drawDuration | 1.0 | Duration for SVG draw | | floatDistance | 8 | Float bob distance (px) | | floatDuration | 2.5 | Float cycle duration (s) | | defaultEase | "power3.out" | Default easing | | respectReducedMotion | true | Respect the OS reduced-motion setting | | dev | false | Enable console warnings for invalid tokens |

Presets

Apply a named motion vocabulary in one line. Explicit options always override the preset.

initAnimations({ preset: 'editorial' });
initAnimations({ preset: 'expressive', movement: 60 }); // override one value

| Preset | Vibe | Reference | |--------|------|-----------| | "minimal" | Restrained, fast — short durations, subtle movement | Linear, Apple | | "editorial" | Polished, purposeful — mirrors the updated base defaults | Stripe, Vercel | | "expressive" | Dramatic, bold — large distances, expo easing | Awwwards / agency | | "playful" | Springy, energetic — back easing, snappy timing | Consumer apps |


Dynamic DOM

motion-tokens sets up a MutationObserver at init. Any element with data-animate added to the DOM later — via a framework router, lazy loading, or manual insertion — is automatically picked up. No manual refresh() call needed.

Accessibility

When the user's OS has prefers-reduced-motion: reduce enabled, all animations collapse to a simple 0.3 s opacity fade — no movement, blur, or scale. The float effect is skipped entirely.

To opt out of this behavior:

initAnimations({ respectReducedMotion: false });

CDN Usage

For projects without a bundler:

<script type="importmap">
{
  "imports": {
    "gsap": "https://cdn.jsdelivr.net/npm/[email protected]/+esm",
    "gsap/ScrollTrigger": "https://cdn.jsdelivr.net/npm/[email protected]/ScrollTrigger.js/+esm",
    "gsap/SplitText": "https://cdn.jsdelivr.net/npm/[email protected]/SplitText.js/+esm"
  }
}
</script>
<script type="module">
  import { initAnimations } from './node_modules/motion-tokens/dist/index.js';
  initAnimations({ dev: true });
</script>

Peer Dependencies

| Package | Version | Notes | |---------|---------|-------| | gsap | >=3.13.0 <3.14.0 | All GSAP plugins (including SplitText) are free since 3.13. 3.14+ has breaking SplitText changes. |

License

MIT