npm package: @mks2508/waapi-animation-primitives
Description: Lightweight, performant React animation primitives using Web Animations API (WAAPI)
Author: MKS2508
Latest version: 0.6.3

<div align="center">

# WAAPI Animation Primitives

**Lightweight, performant React animation components using Web Animations API**

[![npm version](https://badge.fury.io/js/%40mks2508%2Fwaapi-animation-primitives.svg)](https://www.npmjs.com/package/@mks2508/waapi-animation-primitives)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.6-blue)](https://www.typescriptlang.org/)
[![React](https://img.shields.io/badge/React-16.8%2B-blue)](https://react.dev/)
[![Bundle Size](https://img.shields.io/bundlephobia/minzip/%40mks2508%2Fwaapi-animation-primitives)](https://bundlephobia.com/package/@mks2508/waapi-animation-primitives)

[Features](#-features) • [Installation](#-installation) • [Components](#-components) • [Primitives](#-primitives) • [CSS System](#-css-system)

</div>

---

## Features

- **Zero Dependencies** - Only React as a peer dependency
- **WAAPI Powered** - Native 60fps animations using Web Animations API
- **Fully Typed** - Built with TypeScript from the ground up
- **Accessible** - Respects `prefers-reduced-motion` automatically
- **Tree-Shakeable** - Debug code removed in production builds
- **CSS Variables** - Runtime customization without recompilation
- **Locale-Aware** - `Intl.ListFormat` support for localized separators

---

## Installation

```bash
# npm
npm install @mks2508/waapi-animation-primitives

# bun
bun add @mks2508/waapi-animation-primitives

# yarn
yarn add @mks2508/waapi-animation-primitives
```

---

## Components

### SlidingNumber

Animated number transitions with format preservation.

```tsx
import { SlidingNumber } from '@mks2508/waapi-animation-primitives';

<SlidingNumber
  value={1234.56}
  duration={300}
  format={{ decimals: 2, separator: ',' }}
/>
```

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `value` | `number` | *required* | The number to display |
| `duration` | `number` | `200` | Animation duration in ms |
| `format` | `FormatOptions` | `undefined` | Decimal separator options |
| `fontSize` | `string` | `"inherit"` | Font size CSS value |
| `fontWeight` | `string` | `"inherit"` | Font weight CSS value |
| `color` | `string` | `"inherit"` | Text color CSS value |

---

### SlidingText

Character or word-by-word text animations.

```tsx
import { SlidingText } from '@mks2508/waapi-animation-primitives';

<SlidingText
  text="Hello World"
  mode="character"
  direction="vertical"
  staggerDelay={15}
  blur={4}
/>
```

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `text` | `string` | *required* | Text to display |
| `mode` | `'character' \| 'word' \| 'none'` | `'character'` | Animation granularity |
| `direction` | `'vertical' \| 'horizontal'` | `'vertical'` | Animation direction |
| `duration` | `number` | `200` | Animation duration in ms |
| `staggerDelay` | `number` | `15` | Delay between tokens (ms) |
| `blur` | `number` | `0` | Blur effect amount (px) |
| `widthAnimation` | `boolean` | `false` | Animate width changes |
| `initial` | `boolean \| 'initial'` | `true` | Play animation on mount |

---

### AnimatedTokens

Animated token list with coordinated enter/exit transitions.

```tsx
import { AnimatedTokens, Token } from '@mks2508/waapi-animation-primitives';

const tokens: Token[] = [
  { id: '1', text: 'React' },
  { id: '2', text: 'TypeScript' },
  { id: '3', text: 'WAAPI' }
];

<AnimatedTokens
  tokens={tokens}
  maxVisible={5}
  separator=", "
/>
```

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `tokens` | `Token[]` | *required* | Array of `{ id, text }` |
| `maxVisible` | `number` | `undefined` | Max visible before "+N more" |
| `separator` | `string` | `", "` | Separator between tokens |
| `textAnimationMode` | `'character' \| 'word'` | `'character'` | Text animation mode |
| `textDirection` | `'vertical' \| 'horizontal'` | `'vertical'` | Text animation direction |
| `enableWidthAnimation` | `boolean` | `false` | Animate width changes |
| `placeholder` | `string` | `undefined` | Empty state text |

---

### TextFlow

> **Alias for `AnimatedTokensV2`** - Modern version with locale-aware separators and `Reorder` primitive integration.

```tsx
import { TextFlow } from '@mks2508/waapi-animation-primitives';

// en-US: "React, TypeScript, and WAAPI"
// es-ES: "React, TypeScript y WAAPI"
<TextFlow
  tokens={tokens}
  locale="es"
  listType="conjunction"
  maxVisible={5}
/>
```

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `tokens` | `Token[]` | *required* | Array of `{ id, text }` |
| `locale` | `string` | *browser* | Locale for separators |
| `listType` | `'conjunction' \| 'disjunction' \| 'unit'` | `'conjunction'` | List format type |
| `listStyle` | `'long' \| 'short' \| 'narrow'` | `'long'` | List format style |
| `separator` | `string` | `undefined` | Manual separator override |
| `maxVisible` | `number` | `undefined` | Max visible before "+N more" |
| `textAnimationMode` | `'character' \| 'word'` | `'character'` | Text animation mode |
| `textDirection` | `'vertical' \| 'horizontal'` | `'vertical'` | Text animation direction |
| `textStaggerDelay` | `number` | `15` | Stagger delay (ms) |
| `enableWidthAnimation` | `boolean` | `false` | Animate width changes |
| `placeholder` | `string` | `"No tokens"` | Empty state text |

---

## Primitives

### Reorder

Agnostic FLIP animation primitive for animated list reordering.

```tsx
import { Reorder } from '@mks2508/waapi-animation-primitives';

function TodoList() {
  const [todos, setTodos] = useState([
    { id: '1', text: 'Buy milk' },
    { id: '2', text: 'Write code' }
  ]);

  const handleRemove = (id: string) => {
    setTodos(prev => prev.filter(t => t.id !== id));
  };

  return (
    <Reorder
      layout="horizontal"
      stagger={15}
      onItemExit={handleRemove}
    >
      {todos.map(todo => (
        <div key={todo.id}>{todo.text}</div>
      ))}
    </Reorder>
  );
}
```

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `ReactNode` | *required* | Child elements to animate |
| `layout` | `'auto' \| 'horizontal' \| 'vertical' \| 'grid'` | `'auto'` | Layout arrangement |
| `autoAnimate` | `boolean` | `true` | Enable enter animations |
| `stagger` | `number \| { enter, exit }` | `undefined` | Stagger delay (ms) |
| `duration` | `number \| { enter, exit }` | `{ enter: 300, exit: 200 }` | Animation duration (ms) |
| `flipBehavior` | `FLIPBehavior` | `'smooth'` | FLIP animation strategy |
| `exitPositionStrategy` | `ExitPositionStrategy` | `'keep-pace'` | Exit positioning strategy |
| `onItemExit` | `(id: string) => void` | `undefined` | Exit animation start callback |
| `onItemEnter` | `(id: string) => void` | `undefined` | Enter animation complete callback |

---

### useReorder

Low-level hook for orchestration of FLIP animations.

```tsx
import { useReorder } from '@mks2508/waapi-animation-primitives';

function ListManager() {
  const reorder = useReorder({
    enterDuration: 400,
    exitDuration: 200,
    flipDuration: 300,
    onComplete: (id) => console.log('Done:', id)
  });

  // Register element
  <div ref={el => reorder.registerElement('item-1', el)}>Item</div>

  // Trigger animations
  await reorder.startItemExit('item-1');
}
```

---

### useListFormat

Hook wrapper for `Intl.ListFormat` with locale-aware separators.

```tsx
import { useListFormat } from '@mks2508/waapi-animation-primitives';

const parts = useListFormat(
  ['React', 'TypeScript', 'WAAPI'],
  { locale: 'es', type: 'conjunction' }
);
// Returns: [{ type: 'element', index: 0, value: 'React' }, ...]
```

---

## Core

### Animation Orchestrator

Low-level WAAPI orchestration for custom animations.

```tsx
import { AnimationOrchestrator } from '@mks2508/waapi-animation-primitives';

const orchestrator = new AnimationOrchestrator({
  flipDuration: 300,
  flipEasing: 'cubic-bezier(0.2, 0, 0.2, 1)',
  flipBehavior: 'smooth'
});

orchestrator.registerElement('id-1', element);
await orchestrator.startFlip();
```

---

## CSS System

All animation values are controlled via CSS variables.

### Override Globally

```css
:root {
  /* Timing */
  --waapi-duration-enter: 200ms;
  --waapi-duration-exit: 180ms;
  --waapi-duration-flip: 300ms;
  --waapi-stagger-enter: 15ms;

  /* Transforms */
  --waapi-offset-y-enter: 8px;
  --waapi-offset-y-exit: -8px;
  --waapi-scale-exit: 0.95;

  /* Effects */
  --waapi-blur-enter: 4px;
  --waapi-blur-exit: 2px;

  /* Easings */
  --waapi-ease-enter: cubic-bezier(0.33, 1, 0.68, 1);
  --waapi-ease-exit: cubic-bezier(0.32, 0, 0.67, 0);
  --waapi-ease-flip: cubic-bezier(0.2, 0, 0.2, 1);
}
```

### Override Per Component

```tsx
<div style={{ '--waapi-duration-enter': '500ms' }}>
  <AnimatedTokens tokens={tokens} />
</div>
```

### Programmatic Override

```tsx
import { CSS_VAR_NAMES } from '@mks2508/waapi-animation-primitives';

const customStyle = {
  [CSS_VAR_NAMES.durationEnter]: '500ms',
  [CSS_VAR_NAMES.blurEnter]: '8px',
};

<SlidingText text="Hello" style={customStyle} />
```

---

## CSS Classes

| Class | Component | Description |
|-------|-----------|-------------|
| `.waapi-sliding-text-container` | SlidingText | Main container |
| `.waapi-sliding-text-token` | SlidingText | Individual character/word |
| `.waapi-sliding-number-container` | SlidingNumber | Main container |
| `.waapi-animated-tokens-container` | AnimatedTokens | Main container |
| `.waapi-token-wrapper` | AnimatedTokens | Token wrapper |
| `.waapi-token-separator` | AnimatedTokens | Separator |
| `.waapi-token-overflow` | AnimatedTokens | "+N more" counter |

---

## Debug System

Development-only tools. Automatically tree-shaken in production.

```tsx
import { DebugProvider, useDebug } from '@mks2508/waapi-animation-primitives';

<DebugProvider>
  <YourApp />
</DebugProvider>

// Access debug events
const { events, logEvent, exportToCSV } = useDebug();
```

---

## Accessibility

- **Reduced Motion**: Respects `prefers-reduced-motion` media query
- **High Contrast**: Reduces blur effects in high contrast mode
- **Print**: Disables animations for printing

---

## Browser Support

Requires browsers with Web Animations API support:

| Chrome | Firefox | Safari | Edge |
|--------|---------|--------|------|
| 84+ | 75+ | 13.1+ | 84+ |

---

## License

[MIT](./LICENSE) © MKS2508

---

<div align="center">

**[Documentation](#readme)** • **[npm](https://www.npmjs.com/package/@mks2508/waapi-animation-primitives)** • **[GitHub](https://github.com/mks2508/waapi-animation-primitives)**

</div>