# <motion-split>

Split-text reveal primitive. Splits the slotted text into spans and
fades + translates each unit in with a stagger when the first unit
enters view.

**Use it for:** headings and short paragraphs that should come in word by
word, letter by letter or line by line when they scroll into view.

**Avoid it for:** text with links, emphasis or other markup, because the
content is replaced with plain spans. To reveal a block as one piece, use
`motion-reveal`. For units that slide up under a mask, use
`motion-headline`.

**Accessibility:** the split spans get `aria-hidden`, and a visually hidden
span keeps the original text, so screen readers read the whole phrase and
not single letters. Only opacity and transform animate, so the layout does
not shift.

**Reduced motion:** the text is still split, but every unit shows at once
in its final place, with no animation.

**Common mistakes:** expecting `by="lines"` to follow the layout: lines are
measured once, on first render, and are not measured again when the width
changes. Replacing the text later: the new text is not split and does not
animate.

Docs: https://www.motion-components.dev/docs/text/motion-split/

## Install

```js
import 'motion-components/motion-split'
```

Or without a build step:

```html
<script type="module" src="https://cdn.jsdelivr.net/npm/motion-components@1/dist/motion-split.js/+esm"></script>
```

## Example

```html
<motion-split by="chars" interval="0.03" y="30">
  Each character animates in.
</motion-split>
```

## Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `by` | `'words' \| 'chars' \| 'lines'` | `'words'` | Split unit: `'words'`, `'chars'`, or `'lines'`. |
| `interval` | `number` | `0.05` | Stagger between units, in seconds. |
| `duration` | `number` | `0.6` | Spring duration of each unit's reveal, in seconds. |
| `y` | `number` | `20` | Initial vertical offset in pixels for each unit. |
| `once` | `boolean` | `true` | When `true`, only animate the first time the element enters view. Set `once="false"` to turn it off. Ignored with `loop`. |
| `loop` | `boolean` | `false` | Reveal the units, hold, hide them again, then repeat. |
| `hold` | `number` | `1.6` | With `loop`, seconds the text stays revealed before it hides again. |
| `gap` | `number` | `0.5` | With `loop`, seconds the text stays hidden before it reveals again. |
| `pause-on-hover` | `boolean` | `false` | Pause the loop while the pointer is over the text. |

## Properties

| Property | Type | Description |
| --- | --- | --- |
| `playState` | `'idle' \| 'running' \| 'paused' \| 'finished'` | Current state: `'idle'`, `'running'`, `'paused'` or `'finished'`. |
| `finished` | `Promise<void>` | Resolves when the current run settles. A new run replaces the promise. |

## Methods

| Method | Description |
| --- | --- |
| `replay()` | Resets and re-runs the staggered reveal. |
| `play()` | Starts the animation, or resumes it when paused. Resolves once the run settles. Under `prefers-reduced-motion: reduce` the final state is applied immediately without animating. |
| `pause()` | Holds the animation at its current position. No-op unless running. |
| `finish()` | Jumps to the final state and settles. Fires `motion-finish`. |
| `cancel()` | Reverts to the initial state and returns to idle. Fires `motion-cancel`. |

## Events

The `motion-*` events bubble and are composed.

| Event | Description |
| --- | --- |
| `motion-start` | When a run starts. |
| `motion-finish` | When a run finishes, or `finish()` jumps to the end. |
| `motion-cancel` | When `cancel()` stops a run and resets it. |

## Slots

| Slot | Description |
| --- | --- |
| (default) | The text to split. Markup inside is replaced with span units. |
