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

@devepoling-nl/data-motion

v0.0.1

Published

Utility-class syntax for scroll-triggered, in-view, and instant animations via a single data-motion attribute. Thin wrapper around Motion One.

Readme

Data Motion: animate anything with one HTML attribute

npm version license: MIT built on Motion One

data-motion is a lightweight JavaScript animation library that drives scroll, in-view, and instant web animations from a single HTML data-motion attribute. No animation config file, no per-element JS. It's a thin, declarative wrapper around Motion One, the fast, small ( < 5kb) animation library for the web.


<div data-motion="fade-up">Fades and slides up as you scroll to it</div>

Write the animation as a Tailwind-style utility string directly on the HTML element that has it. Framework-agnostic, works in plain HTML, Blade, JSX, Vue templates, or any other server-rendered or client-rendered markup.

Why data-motion

  • One data attribute, zero animation JS to write. data-motion="fade-up" is the whole implementation.
  • Utility-class syntax. Familiar if you already write Tailwind CSS utility classes; compose opacity, scale, x/y, duration, delay, stagger, and easing tokens directly in the attribute.
  • Scroll-triggered, in-view (IntersectionObserver), and instant animation modes in one API.
  • Presets with per-element overrides. Define a fade-up preset once, override duration/delay/ease per element without writing new CSS or JS.
  • Built on Motion One. Hardware-accelerated Web Animations API under the hood, not a custom animation engine.
  • No build step, no bundler config. Plain ESM, tree-shakeable (sideEffects: false), TypeScript types included.

Install

npm install @devepoling-nl/data-motion motion

motion is a peer dependency. Install it alongside.

Quick start

import {initMotion} from "@devepoling-nl/data-motion";

initMotion();

Call initMotion() once your DOM is ready. Nothing runs on import. This package has no import-time side effects, so it's safe to use in SSR, tests, or bundlers that tree-shake on sideEffects: false.


<script type="module">
    import {initMotion} from "@devepoling-nl/data-motion";

    initMotion();
</script>

<div data-motion="fade-up">Hello</div>

Dynamic content

initMotion() skips elements it has already initialised, so call it again after appending new DOM (Livewire morphs, htmx swaps, client-side router changes):

htmx.on("htmx:afterSwap", () => initMotion());

Pass a root to scope the scan instead of scanning the whole document:

initMotion(document.querySelector("#new-section"));

Cleanup

Both initMotion() and applyMotion() return a disposer. Call it to stop running animations / scroll or in-view watchers. Useful before removing elements or on SPA navigation:

const stop = initMotion();
// later
stop();

Presets

import {presets} from "@devepoling-nl/data-motion";

presets["fade-up"] = "scroll-trigger opacity-100 y-[50,0] duration-800 ease-out";
presets["fade-up-children"] = "scroll-trigger opacity-100 y-[50,0] duration-750 stagger-300 ease-out children";

Use a preset by name, and append extra tokens as overrides. They're appended after the preset's own tokens, so they win:


<div data-motion="fade-up"></div>
<div data-motion="fade-up duration-1200 delay-300"></div>
<div data-motion="fade-up trigger-point-30 ease-in-out"></div>
<ul data-motion="fade-up-children stagger-200"></ul>

Add your own presets the same way. presets is a plain mutable object, keyed by whatever name you want to use after data-motion="…".

Full syntax reference

A data-motion value is a space-separated list of tokens. Skip the preset name entirely to write the full utility string:


<div data-motion="scroll-trigger opacity-[0,100] y-[50,0] duration-800 ease-out"></div>

Mode (pick one, default animate)

| Token | Behaviour | |------------------|--------------------------------------------------------------------------------| | scroll-trigger | Animates once, when scroll progress through the element passes trigger-point | | in-view | Animates once, when the element enters the viewport (IntersectionObserver) | | animate | Animates immediately (this is the default if no mode token is given) |

Target

| Token | Behaviour | |------------|--------------------------------------------------------------------------| | children | Animates el.children instead of el itself. Combine with stagger-N |

Timing

| Token | Behaviour | |-------------------|---------------------------------------------------------------------------------------------| | duration-N | Animation duration, N in milliseconds | | delay-N | Start delay, N in milliseconds | | stagger-N | Per-child delay (with children), N in milliseconds | | trigger-point-N | For scroll-trigger/in-view: fire at N% scroll progress / visible amount (default 5) |

Easing

linear ease ease-in ease-out ease-in-out circ-in circ-out circ-in-out back-in back-out back-in-out

Keyframe properties

opacity, scale, y, x. Each takes either a single value (animate to it, from the current value) or a bracketed pair (animate between the two):

y-50            → animate to y: 50
y-[50,0]        → animate from y: 50 to y: 0
opacity-80      → animate to opacity: 0.8   (values >1 treated as a percentage)
opacity-[0,100] → animate from 0 to 1
scale-85        → animate to scale: 0.85
-y-50           → negative: animate to y: -50
-y-[50,0]       → animate from y: -50 to y: 0

A leading - negates the whole value (or both values in a bracket pair). At least one keyframe property is required. A data-motion value with only mode/timing tokens and no opacity/scale/y/x does nothing (and logs a warning).

Preventing a flash of visible content

For scroll-trigger / in-view modes, the animated element sits fully visible in the DOM until its trigger fires, so it can flash into view before snapping to its pre-animation state and fading in. Import the optional base stylesheet to pre-hide the two default presets:

import "@devepoling-nl/data-motion/base.css";

or in CSS directly:

@import "@devepoling-nl/data-motion/base.css";

It only covers exact matches on fade-up and fade-up-children, the presets shipped by default. A custom preset or a raw utility string (data-motion="in-view opacity-[0,100] scale-[85,100]") needs its own matching rule, e.g.:

[data-motion="my-preset"] {
    opacity: 0;
    transform: scale(0.85);
}

Use the preset's first keyframe value as the CSS starting state. If the stylesheet isn't loaded, or the JS never runs, elements simply degrade to visible/normal position. They're never permanently hidden by this alone.

Advanced: single-element API

applyMotion(el, utilityStr) applies a raw (non-preset) utility string to one element directly. Useful for animating something outside the declarative data-motion scan, e.g. an element created purely in JS:

import {applyMotion} from "@devepoling-nl/data-motion";

const stop = applyMotion(myElement, "animate opacity-[0,100] duration-400 ease-out");

Debugging

Unrecognised tokens and unparsable values log a console.warn naming the exact token. Check the console first if an animation isn't firing.

AI / Claude Code

This package ships SKILL.md. Copy or symlink it into your project's .claude/skills/data-motion/ so Claude Code has the full data-motion syntax reference on hand when you ask it to add or edit animations.

mkdir -p .claude/skills/data-motion && cp node_modules/@devepoling-nl/data-motion/SKILL.md .claude/skills/data-motion/SKILL.md

Symlink instead, to stay in sync with the installed package version on update:

mkdir -p .claude/skills/data-motion && ln -sf ../../../node_modules/@devepoling-nl/data-motion/SKILL.md .claude/skills/data-motion/SKILL.md

Related

  • Motion One. The JavaScript animation engine data-motion wraps, built on the native Web Animations API. Use it directly for gestures, drag, and multi-step timelines outside what data-motion's attribute syntax covers.
  • Motion One documentation. duration, delay, easing, and stagger options in data-motion's timing tokens map 1:1 to Motion One's own animation options.

Changelog

See CHANGELOG.md for recent changes.

Contributing

Issues and pull requests are welcome. Open one against the GitHub repo. Keep the core parser dependency-free (only motion as a peer dependency) and add a usage example to the README for any new token.

Security

If you find a security vulnerability, email [email protected] instead of opening a public issue.

Credits

Alternatives

Other libraries that drive animation from HTML attributes or utility syntax, if data-motion isn't the right fit:

  • glaze. Utility-first animation framework built on GSAP, composes animations from data-attribute tokens with responsive variants.
  • AOS (Animate On Scroll). data-aos="fade-up" style scroll animations, CSS-transition based rather than a JS animation engine.
  • ScrollReveal. JS scroll-animation library configured via a JS options object rather than markup.
  • GSAP + ScrollTrigger. Full-featured, code-driven animation and scroll-trigger engine; reach for this when you need timelines, pinning, or scrubbing beyond what a single attribute can express.

Who we are

data-motion is built and maintained by Devepoling, a software development agency in the Netherlands. We build tailored applications, websites, and digital products with a privacy-first approach.

License

MIT