[ @sdxc/u/animation ]
animation
Emits an @keyframes rule under name together with the host animation-* declarations that reference it.
import { animation } from "@sdxc/u/animation";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:
<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:
<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:
<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:
<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:
<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.
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.
u.at("md", u.animation("fade-in", {
keyframes: {
from: { opacity: 0 },
to: { opacity: 1 },
},
duration: "150ms",
easing: "ease-out",
});)Signature
u.animation(name: string, config: AnimationConfig)