# 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
   <div data-viv="@fd @sl-y_ease-out-back" data-viv-on="appearing">…</div>
   ```

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:
   `<ul data-viv="@fd_child-ascend @sl-y" data-viv-on="appearing">`

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
// <div data-viv="@dr" data-viv-out="@fd-o @sl-y-o">
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
