Keyframes
The named checkpoints of an animation: what it looks like at 0%, 50%, 100%, with the browser filling in everything between.
See it
What it is
A keyframe is a snapshot of styles pinned to a percentage of the animation's duration. You write the poses, the browser tweens everything between them. 'from' and 'to' are just aliases for 0% and 100%. The word comes from hand-drawn animation, where the lead artist drew the key poses and assistants drew the in-betweens.
More keyframes means more control and, usually, worse motion. Two or three poses cover most UI. Go dense only when the motion has real beats: a bounce that squashes on contact, a multi-stage loader, a character move. You can also stack percentages on one rule ('0%, 100% { opacity: 1 }') so a loop returns home cleanly.
Gotchas: easing applies between each pair of keyframes, not across the animation as a whole, so a curve gets re-applied at every waypoint and can make smooth motion pulse oddly. A property missing from a keyframe is not held there either: the browser interpolates it between the nearest keyframes on each side that do declare it, and falls back to the element's own underlying value at the ends when nothing else declares it. And transforms behave most predictably when every keyframe lists the same transform functions in the same order. Mismatched lists still animate, but the browser decomposes them into matrices and interpolates that, and the path it picks is rarely the one you had in your head.
Ask AI for it
Write a @keyframes block for this motion with explicit waypoints instead of a plain fade: 0% is opacity 0 with translateY(12px) scale(0.98), 60% is opacity 1 with translateY(-3px) scale(1.01) for a slight overshoot, 100% settles at translateY(0) scale(1). List the same transform functions in the same order in every keyframe so the interpolation stays predictable, animate transform and opacity only, and set animation-fill-mode: forwards so the final pose sticks.