sdxc

[ @sdxc/u/animation ]

keyframes

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

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

Examples

Basic example

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

fade
<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:

bounce
<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:

ping
<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>
<div
  mix={[
    u.keyframes("fade-in", {
      from: { opacity: 0 },
      to: { opacity: 1 },
    }),
    css({
      animationName: "fade-in",
      animationDuration: "150ms",
    }),
  ]}
/>
// 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.

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.

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

Signature

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

Written by Sergio Xalambrí. Follow @sergiodxa for new packages, or sponsor the work.