# <motion-scene>

Scroll-driven scene with sticky positioning and data-driven child animations.
Children declare `data-from`/`data-to` JSON and `data-start`/`data-end` progress
range to interpolate transform and CSS properties on scroll.

**Use it for:** scroll stories where a stage stays pinned while its
children move, scale, rotate or fade as the reader scrolls, such as a
product reveal.

**Avoid it for:** a simple drift on one element; use `motion-parallax`.
For a one-time entrance, use `motion-reveal`. Do not put long text in the
stage: it is one screen tall and cuts off what does not fit.

**Accessibility:** the component adds no roles or aria attributes. Children
stay in the page and in reading order, so screen readers can read them at
any scroll position, even when `data-from` sets `opacity` to 0. The stage
has `overflow: hidden` and is as tall as the viewport or the `container`,
so check the scene with large text and on small screens.

**Reduced motion:** the scroll animation does not start. Children show
their `data-to` end state at once. The scene keeps its `height` and its
sticky stage.

**Common mistakes:** hiding children in CSS, for example with
`opacity: 0`, and showing them only through `data-to`: only the properties
listed in `data-from` are animated, so they stay hidden. Put the start
values in `data-from` instead. Writing `data-from` or `data-to` as invalid
JSON, such as with single quotes: the child is skipped without an error.

Docs: https://www.motion-components.dev/docs/scroll/motion-scene/

## Install

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

Or without a build step:

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

## Example

```html
<motion-scene height="300vh">
  <div
    data-from='{"scale":0.8,"y":"40px"}'
    data-to='{"scale":1,"y":"0px"}'
    data-start="0"
    data-end="0.5"
  >Reveal on scroll</div>
</motion-scene>
```

## Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `height` | `string` | `'200vh'` | Height of the scroll-driven scene (e.g. `"200vh"`, `"150%"`). |
| `pin` | `boolean` | `true` | Whether the inner stage is `position: sticky`. Set `pin="false"` to turn it off. |
| `container` | `string` | `''` | CSS selector for a custom scroll container element (defaults to the document). |

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

## Slots

| Slot | Description |
| --- | --- |
| (default) | Content rendered inside the sticky viewport-stage. Any child with `data-from` and `data-to` attributes will be animated on scroll. |

## CSS custom properties

| Property | Description |
| --- | --- |
| `--mc-progress` | Set by the scene on itself: scroll progress from `0` to `1`. Read it in your own CSS, for example `calc(var(--mc-progress) * 200)`. |
