# <motion-text-mask>

Mask-clip reveal primitive. Clips slotted content behind an `overflow:hidden`
boundary and slides it upward into view — the same pattern used inside
`motion-headline`, exposed as a composable wrapper.

**Use it for:** a heading, a line or a word that should slide up from
behind an invisible edge when it scrolls into view.

**Avoid it for:** content in the first screen that people need at once.
For a heading that splits into lines, words or letters by itself, use
`motion-headline` with `variant="slide"`.

**Accessibility:** the content stays in the page while it is hidden, so
screen readers read it before it animates. Only transform changes, so the
layout does not shift. The host clips its content with `overflow: hidden`,
so a focus outline on a link inside can be cut off; give such links an
outline that stays inside, for example with a negative `outline-offset`.

**Reduced motion:** the content shows in place at once, with no slide.

**Common mistakes:** wrapping a whole paragraph in one mask: the host is
`inline-block`, so the paragraph slides up as one block, not line by line.
Use one mask per line, or `motion-headline` with `by="lines"`.

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

## Install

```js
import 'motion-components/motion-text-mask'
```

Or without a build step:

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

## Example

```html
<motion-text-mask duration="1.1">Your headline text</motion-text-mask>
```

## Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `duration` | `number` | `0.9` | Spring duration of the slide-up reveal, in seconds. |
| `delay` | `number` | `0` | Delay before the reveal starts, 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` | Slide the content in, hold, slide it back out, then repeat. |
| `hold` | `number` | `1.6` | With `loop`, seconds the content stays visible before it slides back out. |
| `gap` | `number` | `0.5` | With `loop`, seconds the content stays hidden before it slides in again. |
| `pause-on-hover` | `boolean` | `false` | Pause the loop while the pointer is over the mask. |

## 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 the inner mask offset and re-runs the 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 or inline content to reveal under the mask. |
