# <motion-icon>

Animates any SVG icon on a spring: it draws the strokes in, or makes the
icon pop, bounce, rotate, wiggle, pulse or slide up, down, left or right.
It can also do both at once, as in `animation="draw wiggle"`. Every run
starts from where the icon is, so hovering again mid-animation never
makes it jump. With `trigger="hover"`, a slide holds while the pointer is
over it and springs back on leave. Pass the icon as a URL in `src`, as an SVG
string in `icon`, or as a child `<svg>`. Stroke sets such as Lucide,
Tabler, Heroicons and Iconoir can draw; filled icons such as Phosphor and
Bootstrap pop instead. The icon takes `currentColor`. Inside a button, a
link or a `<summary>`, `hover` and `click` follow that element.

**Use it for:** icons that draw in or move when people hover or click a
button or link, when they scroll into view, or once when the page loads.
It works with any SVG icon.

**Avoid it for:** icons that switch between two states, such as menu and
close or play and pause; use `motion-icon-state`. Avoid `trigger="loop"`
next to text people need to read, because the loop never stops on its own.

**Accessibility:** without `label` the icon is decorative and gets
`aria-hidden="true"`. With `label` it gets `role="img"` and that name as
`aria-label`. The icon is never focusable, and `trigger="hover"` reacts to
the pointer only, not to keyboard focus. Inside a button or link with
visible text, leave `label` empty. An icon-only button needs a name: set
`aria-label` on the button, or `label` on the icon.

**Reduced motion:** the icon shows its final state at once, with no
animation. With `draw` and `trigger="view"`, the icon stays hidden until it
scrolls into view and then appears at once.

**Common mistakes:** using `animation="draw"` with a filled icon, such as
Phosphor or Bootstrap: there are no strokes to draw, so it pops instead.
Setting `label` on an icon inside a button that already has text, so
screen readers read the name twice.
Listing two motions, such as `animation="pop wiggle"`: only `draw` combines
with a motion, so only the first motion runs.

Docs: https://www.motion-components.dev/docs/icons/motion-icon/

## Install

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

Or without a build step:

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

## Example

```html
<motion-icon src="https://cdn.jsdelivr.net/npm/lucide-static@1/icons/heart.svg"></motion-icon>
```

## Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `src` | `string` | `''` | URL of an SVG file, for example from a CDN or your own `/icons` folder. Responses are cached per URL. |
| `icon` | `string` | `''` | SVG markup to render, for example `import { Heart } from 'lucide-static'`. Takes precedence over `src`. |
| `animation` | `IconAnimation` | `'draw'` | `'draw'`, `'pop'`, `'bounce'`, `'rotate'`, `'wiggle'`, `'pulse'`, `'slide-up'`, `'slide-down'`, `'slide-left'` or `'slide-right'`, or `draw` plus one of the others, such as `'draw slide-right'`, to run both. `draw` needs a stroke icon and falls back to `pop`. |
| `trigger` | `'hover' \| 'click' \| 'view' \| 'mount' \| 'loop'` | `'hover'` | What starts the animation: `'hover'`, `'click'`, `'view'` (scrolled into view), `'mount'` or `'loop'`. |
| `duration` | `number` | `0.6` | Duration of one run, in seconds. |
| `bounce` | `number` | `0.4` | Spring bounciness as the icon settles (0 = no overshoot). `wiggle` always uses at least `0.6`. |
| `delay` | `number` | `0` | Seconds to wait before the animation starts. |
| `interval` | `number` | `1.2` | Seconds to pause between runs when `trigger="loop"`. |
| `once` | `boolean` | `true` | With `trigger="view"`, only animate the first time. Set `once="false"` to replay on every entry. |
| `label` | `string` | `''` | Accessible name. Without it the icon is decorative and hidden from screen readers. |

## 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()` | Restarts the animation from its starting state. |
| `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 |
| --- | --- |
| `error` | When `src` cannot be loaded. |
| `motion-start` | When an animation run starts. |
| `motion-finish` | When an animation run finishes. |
| `motion-cancel` | When `cancel()` stops a run and resets it. |

## Slots

| Slot | Description |
| --- | --- |
| (default) | An inline `<svg>` icon. Shown while `src` loads and ignored once `icon` or `src` renders. |

## CSS custom properties

| Property | Description |
| --- | --- |
| `--mc-icon-size` | Width and height of the icon. Default `1.5em`. |
| `--mc-icon-color` | Icon color. Default `currentColor`. |
| `--mc-icon-fill` | Fills the inside of an outline icon. Default `none`. |

## CSS parts

| Part | Description |
| --- | --- |
| `icon` | The wrapper around the rendered `icon` SVG. |
