# <motion-headline>

Animated headline with split-text reveal. Splits the text content by
words, characters, or lines, then reveals each unit with a stagger.
`variant="slide"` slides each unit up under a mask; `variant="flip"`
flips each unit on the X axis with perspective.

**Use it for:** page and section headings that reveal word by word, letter
by letter or line by line as they scroll into view.

**Avoid it for:** headings with links or other markup, because the content
is replaced with plain text. For cards and images, use `motion-reveal`. For
lists, use `motion-stagger`.

**Accessibility:** the split pieces are `aria-hidden`, and a visually hidden
span keeps the full text, so screen readers read it in one piece. The
element has no heading role, so put it inside a heading element such as
`<h2>`.

**Reduced motion:** the text shows in its final place at once, with no
animation. The element does not wait to scroll into view.

**Common mistakes:** using `by="lines"` where the width can change. The
lines are measured once, when the element first renders, and are not split
again on resize. Changing `by` or `variant` after the first render has no
effect. `variant="flip"` splits by words when `by="lines"`.

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

## Install

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

Or without a build step:

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

## Example

```html
<motion-headline by="words" variant="slide" interval="0.08">
  Motion-first web components
</motion-headline>
```

## Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `by` | `'words' \| 'chars' \| 'lines'` | `'words'` | Split unit: `'words'`, `'chars'`, or `'lines'`. |
| `variant` | `'slide' \| 'flip'` | `'slide'` | Reveal style: `'slide'` (mask + slide up) or `'flip'` (3D rotateX). |
| `interval` | `number` | `0.06` | Stagger between units, in seconds. |
| `duration` | `number` | `1` | Spring duration of each unit's reveal, in seconds. |
| `delay` | `number` | `0` | Delay before the first unit reveals, in seconds. |
| `threshold` | `number` | `0.2` | IntersectionObserver threshold (0–1) at which the reveal triggers. |
| `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 headline, hold, hide it again, then repeat. |
| `hold` | `number` | `1.6` | With `loop`, seconds the headline stays visible before it hides again. |
| `gap` | `number` | `0.5` | With `loop`, seconds the headline stays hidden before it reveals again. |
| `pause-on-hover` | `boolean` | `false` | Pause the loop while the pointer is over the headline. |

## 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 headline 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 headline text. Plain text only — markup inside is replaced. |
