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

vivace-css

v1.0.0

Published

Attribute-driven CSS animation library with a tiny trigger engine — compose animations in HTML with data-viv.

Readme

Vivace

Attribute-driven CSS animations with a tiny, dependency-free trigger engine. Compose animations directly in your HTML — no keyframes to write, no wrapper components, works in plain HTML, React, Svelte, Vue, anything that renders DOM.

<div data-viv="@fd @sl-y_ease-out-back" data-viv-on="appearing">
  Fades and slides up when scrolled into view.
</div>

Install

bun add vivace-css   # or npm / pnpm / yarn
import Vivace from 'vivace-css'
import 'vivace-css/vivace.css'

Vivace.init()

That's it. Every [data-viv] element on the page — including ones mounted later by your framework — is picked up automatically.

The grammar

data-viv holds a composition of keys (animations, prefixed @) and modifiers (tuning, prefixed _). Modifiers concatenate straight onto their key (the _ delimits them); separate keys are space-separated — "@fd @sl-y_ease-out-back":

<button data-viv="@pop_ease-out-back" data-viv-on="click">Pop!</button>

Keys

| Key | Animation | Variants | | --- | --- | --- | | @fd | Fade | @fd in, @fd-o out | | @sl | Slide | @sl-x, @sl-y, -o out, ! flip direction | | @rt | Rotate | @rt-x/y/z full spin, -i/-o 90° fold, ! flip | | @sc | Scale | @sc-i grow in, @sc-o shrink out, per-axis -x/-y, ! from 2x | | @sp | Spiral | @sp-i, @sp-o, ! flip | | @vb | Vibrate | @vb | | @pr | Perspective | @pr-i, @pr-o fly along Z, ! flip, -up/-down distance | | @bn | Bounce | @bn both axes, @bn-x, @bn-y, ! invert | | @sh | Shake | @sh-x, @sh-y, ! invert | | @fl | Flick | @fl-x/y/z spring settle, -o fold away | | @bl | Blur | soft-focus fade in, @bl-o out — new in Vivace | | @sw | Swing | pendulum from the top edge, ! flip — new in Vivace | | @pop | Pop | elastic scale-in punch — new in Vivace | | @dr | Drop | fall in and bounce on landing, ! rise — new in Vivace | | @sq | Squash | cartoon squash & stretch, ! starts tall — new in Vivace | | @hb | Heartbeat | double scale pulse — new in Vivace | | @wv | Wave | settling vertical bob, great with _child — new in Vivace | | @gl | Glow | currentColor drop-shadow bloom, _alt pulses — new in Vivace |

Keys compose: @fd @sl-y fades while sliding. @bn @sl-x slides in with a bounce landing.

Modifiers

  • _ease-* — lin, in, out, in-out, circ, back, expo (+ in-/out- forms)
  • _delay-1 … _delay-8 — start delay in 0.1s steps
  • _speed-up[+++] / _speed-down[+++] — faster / slower
  • _lv-up[+++] / _lv-down[+++] — intensity (distances, angles, spring size)
  • _origin-t/r/b/l/lt/rt/rb/lb — transform origin
  • _repeat[-2..5] — iteration count (bare = infinite)
  • _alt, _alt-rev, _rev — playback direction
  • _child-ascend / _child-descend / _child-odd / _child-even — animate direct children with a stagger instead of the element itself

Triggers

data-viv-on decides when the composition fires:

| Trigger | Fires | | --- | --- | | load (default) | on mount | | appearing | when scrolled into view (one shared IntersectionObserver); add data-viv-repeat-scroll to replay every time | | hover | on pointer enter (delegated, no per-element listeners) | | click | on click/tap (delegated) | | focus | on focus — great for form fields |

Fine tuning without CSS

<div data-viv="@sl-y" data-viv-duration="600ms" data-viv-delay="0.2" data-viv-ease="out-expo" data-viv-repeat="2">

Or drop down to the raw custom properties in style (--AD duration, --AE easing, --AL intensity, …) for full control.

API

Vivace.init(root?)     // scan + arm; idempotent, defaults to document.body
Vivace.trigger(el)     // fire (or restart) regardless of trigger type
Vivace.play(el)        // unpause, or fire if it never ran
Vivace.pause(el)       // freeze in place
await Vivace.out(el)   // play data-viv-out (default '@fd-o'), resolve when done
Vivace.defineKey(token, { keyframe, duration, vars })  // runtime @key
Vivace.defineTrigger(name, (el, fire) => teardown)     // custom data-viv-on
Vivace.destroy()       // tear down observers/listeners (SPA cleanup)

Animated elements dispatch bubbling vivace:play, vivace:out and vivace:end events.

Exit animations

<div data-viv="@dr" data-viv-out="@fd-o @sl-y-o">…</div>
await Vivace.out(el)  // fill-mode keeps it hidden — remove without flash
el.remove()

SCSS

The shipped vivace.css is compiled from SCSS with a plugin-per-file layout (inherited from A.css): every key lives in src/styles/keys/, every modifier group in src/styles/modifiers/.

Consuming the source lets you re-configure the attribute name, spring table, stagger size and more:

@use 'vivace-css/scss' with ($child-count: 20);

Custom bundles — write your own entry with only the plugins you want:

@use 'vivace-css/src/styles/keyframes';
@use 'vivace-css/src/styles/base';
@use 'vivace-css/src/styles/keys/fade';
@use 'vivace-css/src/styles/keys/slide';
@use 'vivace-css/src/styles/modifiers/easing';

Adding an animation key — drop a file in keys/ that fills variable slots (pick the 2-step or 10-step keyframe), add one @use line to keys/_index.scss, done. The engine picks it up with zero JS changes:

// keys/_wobble.scss
@use '../config' as c;
@use '../mixins' as m;

@include m.key('@wb') {
  --AN: #{c.$kf-10};
  @for $i from 0 through 9 {
    --ARZ#{$i}: calc(#{m.alt($i) * 20deg} * var(--ASP#{$i}));
  }
}

Accessibility

prefers-reduced-motion: reduce collapses every animation to an instant snap — content is never hidden from users who opt out of motion.

Docs & playground

Full documentation and the interactive composer live in the docs app — bun run docs:dev from the repo root.