# <motion-curve>

Sine-wave text. Each character of `text` rides a travelling wave with a
tangent-aligned rotation, producing a flowing serpent effect. With `loop`,
the text scrolls horizontally as a marquee while still riding the wave.

Text can be set with the `text` attribute or as child text. Child text
doubles as a pre-upgrade fallback: the browser shows it before the
element is defined, so the page never paints an empty gap. For long
marquee strings prefer the `text` attribute so the fallback does not
wrap across several lines.

**Use it for:** short words and headlines that ride a gentle wave, and,
with `loop`, a banner that scrolls while it waves.

**Avoid it for:** body text and long sentences that people need to read.
For text on a fixed curve, use `motion-arc`. For a marquee that keyboard
users can pause, use `motion-ticker`.

**Accessibility:** screen readers read the whole text once from a visually
hidden copy. The moving letters, and the repeated copies in `loop` mode,
are `aria-hidden`. The wave never stops on its own, and `pause-on-hover`
works with a mouse only. If people must be able to stop it, add a button
that calls `pause()`.

**Reduced motion:** the wave does not start. The letters stay on a straight
line and, in `loop` mode, the text does not scroll.

**Common mistakes:** changing the child text after the element is on the
page. The child text is read once, when the element connects; set the
`text` property instead. Using `no-pad` without room above and below, so
the wave overlaps the content around it.

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

## Install

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

Or without a build step:

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

## Example

```html
<motion-curve amplitude="20" wave-duration="2">Motion</motion-curve>
<motion-curve text="endless" loop loop-speed="60"></motion-curve>
```

## Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `text` | `string \| undefined` |  | Text to render along the wave. Falls back to the element's child text when unset. |
| `amplitude` | `number` | `24` | Peak vertical offset of the wave, in pixels. |
| `wave-length` | `number` | `280` | Distance between wave crests, in pixels. |
| `wave-duration` | `number` | `2.5` | Seconds for one full wave cycle. Lower is faster, `0` holds the wave still. |
| `loop` | `boolean` | `false` | When `true`, scroll the text horizontally as a marquee. |
| `loop-speed` | `number` | `80` | Scroll speed when `loop` is enabled, in pixels per second. |
| `loop-gap` | `number` | `48` | Gap between repeated text sets in marquee mode, in pixels. This is spacing, not the seconds-based `gap` other components use for loop timing. |
| `no-pad` | `boolean` | `false` | When `true`, omit vertical padding equal to `amplitude`. |
| `pause-on-hover` | `boolean` | `false` | When `true`, pause the wave/loop while the cursor is over the element. |

## 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 |
| --- | --- |
| `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. |
