# Vivace — attribute-driven CSS animations
> Vivace is a zero-dependency CSS animation library (under 7 kB gzipped,
> CSS + JS) controlled entirely through `data-viv` attributes. A tiny
> TypeScript engine handles WHEN animations fire (scroll, hover, click,
> focus, mount); generated CSS handles HOW. It works in plain HTML and in
> every framework (React, Svelte, Vue, …) because it only reads attributes
> — there are no wrapper components.
This file is the complete documentation as one markdown document, intended
for AI agents and LLMs. Human-readable docs: https://usevivace.dev/docs
Package: `vivace` on npm. License: MIT.
## Instructions for AI agents
When asked to add animations to a project using Vivace:
1. Install `vivace` (bun/npm/pnpm/yarn), then initialize ONCE at the app
root — never per component:
```ts
import Vivace from 'vivace-css'
import 'vivace-css/vivace.css'
Vivace.init()
```
In SSR frameworks run `init()` client-side only (SvelteKit: `$effect`
in the root layout; React/Next: `useEffect` in a client component; Vue:
`onMounted`). The module itself is SSR-safe to import. Return
`Vivace.destroy()` from the lifecycle cleanup.
2. Animate by writing attributes — do NOT write @keyframes, wrapper
components, or JS animation code:
```html
…
```
3. Elements mounted later (SPA route changes, v-if, conditional rendering)
register automatically via MutationObserver — no extra calls.
4. Composition notation rules (IMPORTANT):
- Keys start with `@` (animations); modifiers start with `_` (tuning).
- Modifiers concatenate directly onto the key they tune, no separator:
`@sl-y_ease-out-back_delay-2`.
- Separate keys are space-separated: `@fd @sl-y_ease-out-back`.
- Never write `@fd@fd` (missing space) or use `|` separators.
- Variant grammar: `-x/-y/-z` axis, `-i` enter, `-o` exit, trailing
`!` inverts direction: `@sl-y!` slides from below.
- CSS matching is substring-based; the notation above is canonical.
5. For lists/grids, put ONE `data-viv` with a `_child-*` modifier on the
PARENT; its direct children animate staggered:
``
6. Respect reduced motion: nothing to do — `prefers-reduced-motion` support
is built in (animations collapse to an instant snap; content never hides).
## Attributes
| Attribute | Purpose |
| --- | --- |
| `data-viv` | The composition: keys + modifiers (see notation rules) |
| `data-viv-on` | Trigger: `load` (default), `appearing`, `hover`, `click`, `focus` |
| `data-viv-out` | Exit composition played by `Vivace.out()` (default `@fd-o`) |
| `data-viv-repeat-scroll` | With `appearing`: re-arm every time it leaves the viewport |
| `data-viv-duration` | Duration (`600ms`, `0.6s` or `0.6`) → `--AD` |
| `data-viv-delay` | Start delay → `--ADL` |
| `data-viv-ease` | Easing: keyword, `cubic-bezier()`, or named curve (`out-back`, `in-expo`, `circ`, …) → `--AE` |
| `data-viv-repeat` | Iteration count (number or `infinite`) → `--AC` |
Raw CSS custom properties (inline style) for full control: `--AD` duration
base (s), `--AS` speed multiplier, `--AE` easing, `--AC` count, `--ADL`
delay, `--AL` intensity level (scales distances/angles/springs), `--AOXY`
transform origin.
## Triggers
- `load` — Fires as soon as the element mounts.
- `appearing` — Fires when the element scrolls into view.
- `hover` — Fires on pointer enter.
- `click` — Fires on click or tap.
- `focus` — Fires when the element receives focus.
All triggers are delegated (one shared IntersectionObserver, one listener
set on the document) — per-element cost is zero.
## Animation keys (18)
### `@fd` — Fade
Opacity in (default) or out.
Variants: `@fd`, `@fd-o`
### `@sl` — Slide
Translate in/out along one axis; ! flips the direction.
Variants: `@sl-x`, `@sl-x!`, `@sl-y`, `@sl-y!`, `@sl-x-o`, `@sl-x-o!`, `@sl-y-o`, `@sl-y-o!`
### `@rt` — Rotate
Full 360° spin per axis, or 90° fold in/out.
Variants: `@rt-x`, `@rt-y`, `@rt-z`, `@rt-x-i`, `@rt-y-i`, `@rt-z-i`, `@rt-x-o`, `@rt-y-o`, `@rt-z-o`, `@rt-z!`
### `@sc` — Scale
Grow from zero or shrink away; ! scales from 2x instead.
Variants: `@sc-i`, `@sc-i!`, `@sc-o`, `@sc-o!`, `@sc-x-i`, `@sc-y-i`, `@sc-x-o`, `@sc-y-o`
### `@sp` — Spiral
Slide in/out while orbiting a full turn.
Variants: `@sp-i`, `@sp-i!`, `@sp-o`, `@sp-o!`
### `@vb` — Vibrate
Rapid multi-turn buzz around the center.
Variants: `@vb`
### `@pr` — Perspective
Fly in from deep Z space toward the viewer.
Variants: `@pr-i`, `@pr-i!`, `@pr-o`, `@pr-o!`, `@pr-up`, `@pr-down`
### `@bn` — Bounce
Elastic scale spring; bare token bounces both axes.
Variants: `@bn`, `@bn!`, `@bn-x`, `@bn-y`
### `@sh` — Shake
Decaying positional shake along one axis.
Variants: `@sh-x`, `@sh-x!`, `@sh-y`, `@sh-y!`
### `@fl` — Flick
Rotational spring settle, like a flicked card.
Variants: `@fl-x`, `@fl-y`, `@fl-z`, `@fl-x-o`, `@fl-y-o`, `@fl-z-o`
### `@bl` — Blur *(Vivace original)*
Soft-focus fade: blurred and transparent to sharp.
Variants: `@bl`, `@bl-o`
### `@sw` — Swing *(Vivace original)*
Pendulum sway hinged on the top edge.
Variants: `@sw`, `@sw!`
### `@pop` — Pop *(Vivace original)*
Scale-in with an elastic overshoot punch.
Variants: `@pop`
### `@dr` — Drop *(Vivace original)*
Fall in from above and bounce on landing; ! rises from below.
Variants: `@dr`, `@dr!`
### `@sq` — Squash *(Vivace original)*
Cartoon squash & stretch; ! starts stretched tall.
Variants: `@sq`, `@sq!`
### `@hb` — Heartbeat *(Vivace original)*
Two scale pulses, the second softer. Pair with _repeat.
Variants: `@hb`
### `@wv` — Wave *(Vivace original)*
Vertical bob that settles; shines staggered across children.
Variants: `@wv`, `@wv!`
### `@gl` — Glow *(Vivace original)*
Drop-shadow bloom in the element’s own color; _alt makes it pulse.
Variants: `@gl`, `@gl-o`
Keys compose freely in one attribute: `@fd @sl-y` fades while sliding;
`@bn @sl-x` slides in with a bounce landing.
## Modifiers (8 groups)
### `_ease` — Easing
Timing curve for the whole composition.
Variants: `_ease-lin`, `_ease-in`, `_ease-out`, `_ease-in-out`, `_ease-circ`, `_ease-in-circ`, `_ease-out-circ`, `_ease-back`, `_ease-in-back`, `_ease-out-back`, `_ease-expo`, `_ease-in-expo`, `_ease-out-expo`
### `_delay` — Delay
Start delay in 0.1s steps.
Variants: `_delay-1`, `_delay-2`, `_delay-3`, `_delay-4`, `_delay-5`, `_delay-6`, `_delay-7`, `_delay-8`
### `_speed` — Speed
Duration multiplier: up is faster, down is slower.
Variants: `_speed-up`, `_speed-up+`, `_speed-up++`, `_speed-up+++`, `_speed-down`, `_speed-down+`, `_speed-down++`, `_speed-down+++`
### `_lv` — Level
Intensity: scales distances, angles and spring amplitude.
Variants: `_lv-up`, `_lv-up+`, `_lv-up++`, `_lv-up+++`, `_lv-down`, `_lv-down+`, `_lv-down++`, `_lv-down+++`
### `_origin` — Origin
Transform origin: t/r/b/l and corners (lt, rt, rb, lb).
Variants: `_origin-t`, `_origin-rt`, `_origin-r`, `_origin-rb`, `_origin-b`, `_origin-lb`, `_origin-l`, `_origin-lt`
### `_repeat` — Repeat
Iteration count; bare token loops forever.
Variants: `_repeat`, `_repeat-2`, `_repeat-3`, `_repeat-4`, `_repeat-5`
### `_alt` — Direction
Alternate (there-and-back) or reversed playback.
Variants: `_alt`, `_alt-rev`, `_rev`
### `_child` — Children
Animate direct children instead, staggered in sequence.
Variants: `_child-ascend`, `_child-descend`, `_child-odd`, `_child-even`, `_child-ascend-up`, `_child-ascend-down`
## JavaScript API
```ts
Vivace.init(root?) // scan + arm; idempotent; default 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, resolve when finished
Vivace.defineKey(token, def) // runtime key: {keyframe: 2|10, duration?, vars}
Vivace.defineTrigger(name, setup) // custom data-viv-on; setup(el, fire) => teardown
Vivace.destroy() // remove all listeners/observers
```
Exit pattern (toasts, modals, list removal):
```ts
//
await Vivace.out(el) // fill-mode keeps it hidden afterwards
el.remove() // no flash
```
Lifecycle events (bubbling CustomEvents on animated elements):
`vivace:play`, `vivace:out`, `vivace:end`.
Custom key example:
```ts
Vivace.defineKey('@wb', {
keyframe: 10, // 2 = simple in/out slots, 10 = spring/oscillation slots
vars: { '--ARZ1': '8deg', '--ARZ3': '-6deg', '--ARZ5': '3deg' }
})
```
Slot vocabulary for defineKey / SCSS keys: `--ATX0..10`/`--ATY*` translate,
`--ASX*`/`--ASY*` scale, `--ARX/Y/Z*` rotate, `--AOP0/1` opacity,
`--AF0/1` filter. Scale amplitudes by `var(--ASPn)` (spring table) and
`var(--AL)` so `_lv-*` modifiers keep working.
## SCSS source
`vivace/scss` exposes the source with a plugin-per-file layout: one file
per key in `keys/`, per modifier group in `modifiers/`. Configure via
`@use 'vivace-css/scss' with ($child-count: 20)`. Custom bundles: write an
entry that @uses `keyframes`, `base` and only the plugins you want.
## Links
- Docs: https://usevivace.dev/docs
- Playground (compose visually, copy the attribute): https://usevivace.dev/playground
- Community gallery: https://usevivace.dev/gallery
- Repository: https://github.com/Diary-Axel-RAKOTOARIVAO/vivace