# keyframes

> Emits an `@keyframes` rule under `name` at the top level, where the serializer reads stop keys (`from`, `to`, `50%`) as stop selectors.

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

Emits an `@keyframes` rule under `name` at the top level, where the
serializer reads stop keys (`from`, `to`, `50%`) as stop selectors. Pair it
with a `css()` call that sets `animationName` and `animationDuration`.

## Examples

### Basic example

Use `keyframes(name, frames)` to declare a `@keyframes` rule with `from` and `to` stops, and run it by name with `animationHost()`:

```tsx
<div mix={[...]}>
	<div
		mix={[
			keyframes("fade", { from: { opacity: 1 }, to: { opacity: 0.2 } }),
			animationHost("fade", {
				duration: "1s",
				iterationCount: "infinite",
				direction: "alternate",
			}),
			...,
		]}
	>
		fade
	</div>
</div>
```

### Using percentage stops

Key the frames by percentage, like `"50%"`, to place stops anywhere on the timeline; a stop may set its own easing:

```tsx
<div mix={[...]}>
	<div
		mix={[
			keyframes("bounce", {
				"0%, 100%": {
					translate: "0 -25%",
					animationTimingFunction: "cubic-bezier(0.8, 0, 1, 1)",
				},
				"50%": { translate: "0 0", animationTimingFunction: "cubic-bezier(0, 0, 0.2, 1)" },
			}),
			animationHost("bounce", { duration: "1s", iterationCount: "infinite" }),
			...,
		]}
	>
		bounce
	</div>
</div>
```

### Animating several properties

A stop can set several properties, like `scale` and `opacity` together for a ping:

```tsx
<div mix={[...]}>
	<div mix={[relative(), ...]}>
		<span
			mix={[
				keyframes("ping", { "75%, 100%": { scale: "2", opacity: 0 } }),
				animationHost("ping", {
					duration: "1s",
					easing: "cubic-bezier(0, 0, 0.2, 1)",
					iterationCount: "infinite",
				}),
				absolute(),
				inset(0),
				...,
			]}
		/>
		ping
	</div>
</div>
```

```ts
<div
  mix={[
    u.keyframes("fade-in", {
      from: { opacity: 0 },
      to: { opacity: 1 },
    }),
    css({
      animationName: "fade-in",
      animationDuration: "150ms",
    }),
  ]}
/>
```

```ts
// Equivalent at the top level of a `css()` call.
css({ "@keyframes fade-in": { from: { opacity: 0 }, to: { opacity: 1 } } })
```

## Applying on a state

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

```ts
u.hover(<div
  mix={[
    u.keyframes("fade-in", {
      from: { opacity: 0 },
      to: { opacity: 1 },
    }),
    css({
      animationName: "fade-in",
      animationDuration: "150ms",
    }),
  ]}
/>)
```

## Responsive design

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

```ts
u.at("md", <div
  mix={[
    u.keyframes("fade-in", {
      from: { opacity: 0 },
      to: { opacity: 1 },
    }),
    css({
      animationName: "fade-in",
      animationDuration: "150ms",
    }),
  ]}
/>)
```

## Signature

```ts
u.keyframes(name: string, frames: Record<string, CSSStyles>)
```
