# animation

> Emits an `@keyframes` rule under `name` together with the host `animation-*` declarations that reference it.

```ts
import { animation } from "@sdxc/u/animation";
```

Emits an `@keyframes` rule under `name` together with the host
`animation-*` declarations that reference it. Use the named form when the
animation name is useful for debugging in devtools.

## Quick reference

Every documented call, beside the CSS it emits.

| Call | CSS |
| --- | --- |
| `u.animation("fade-in", { keyframes: { from: { opacity: 0 }, to: { opacity: 1 }, }, duration: "150ms", easing: "ease-out", });` | `css({ "@keyframes fade-in": { from: { opacity: 0 }, to: { opacity: 1 } }, animationName: "fade-in", animationDuration: "150ms", animationTimingFunction: "ease-out", })` |
| `u.animation("fade-in", { keyframes: { from: { opacity: 0 }, to: { opacity: 1 } }, duration: "150ms", delay: "150ms", });` | `css({ "@keyframes fade-in": { from: { opacity: 0 }, to: { opacity: 1 } }, animationName: "fade-in", animationDuration: "150ms", animationDelay: "150ms", })` |

## Examples

### Basic example

Use `animation(name, config)` with `keyframes` and a `duration` to animate an element; `iterationCount: "infinite"` keeps it running:

```tsx
<div mix={[...]}>
	<div
		mix={[
			animation("spin", {
				keyframes: { from: { rotate: "0deg" }, to: { rotate: "360deg" } },
				duration: "1s",
				easing: "linear",
				iterationCount: "infinite",
			}),
			...,
		]}
	>
		spin
	</div>
</div>
```

### Generating the name

Leave the name out and pass only the config, and a stable name is generated from the keyframes:

```tsx
<div mix={[...]}>
	<div
		mix={[
			animation({
				keyframes: { "0%, 100%": { opacity: 1 }, "50%": { opacity: 0.4 } },
				duration: "2s",
				easing: "ease-in-out",
				iterationCount: "infinite",
			}),
			...,
		]}
	>
		pulse
	</div>
</div>
```

### Alternating direction

Set `direction: "alternate"` to play every other cycle in reverse, so keyframes written one way swing back and forth:

```tsx
<div mix={[...]}>
	<div
		mix={[
			animation("sway", {
				keyframes: { from: { translate: "-2rem 0" }, to: { translate: "2rem 0" } },
				duration: "800ms",
				easing: "ease-in-out",
				iterationCount: "infinite",
				direction: "alternate",
			}),
			...,
		]}
	>
		sway
	</div>
</div>
```

### Delaying and holding frames

Set `delay` and `fillMode: "both"` for an entrance that stays hidden while it waits and keeps its final frame. Reload the frame to replay it:

```tsx
<div mix={[...]}>
	<div
		mix={[
			animation("enter", {
				keyframes: {
					from: { opacity: 0, translate: "0 2rem" },
					to: { opacity: 1, translate: "0 0" },
				},
				duration: "600ms",
				easing: "ease-out",
				delay: "500ms",
				fillMode: "both",
			}),
			...,
		]}
	>
		enter
	</div>
</div>
```

### Driving it with scroll

Set `timeline: "view()"` to drive the animation by the element's position in its scroll container instead of time. Scroll the box to reveal each card:

```tsx
<div mix={[scroll("y"), bs(64), ...]}>
	<div mix={[...]}>
		{["One", "Two", "Three", "Four", "Five", "Six"].map((label) => (
			<div
				mix={[
					animation("reveal", {
						keyframes: {
							from: { opacity: 0, scale: "0.8" },
							to: { opacity: 1, scale: "1" },
						},
						duration: "auto",
						easing: "linear",
						fillMode: "both",
						timeline: "view()",
						range: "entry 0% entry 100%",
					}),
					...,
				]}
			>
				Card {label}
			</div>
		))}
	</div>
</div>
```

## Applying on a state

Wrap the call in a state utility to scope it to one selector.

```ts
u.hover(u.animation("fade-in", {
  keyframes: {
    from: { opacity: 0 },
    to: { opacity: 1 },
  },
  duration: "150ms",
  easing: "ease-out",
});)
```

## Responsive design

Wrap the call in a container query to scope it to one width.

```ts
u.at("md", u.animation("fade-in", {
  keyframes: {
    from: { opacity: 0 },
    to: { opacity: 1 },
  },
  duration: "150ms",
  easing: "ease-out",
});)
```

## Signature

```ts
u.animation(name: string, config: AnimationConfig)
```
