Keyframes and the shorthand are separate things
An animation is two pieces. The @keyframes block describes what changes. The animation shorthand on the element says how it runs — duration, timing function, delay, iteration count, direction, fill mode.
The shorthand is where most of the character comes from. The same keyframes at 200ms with an ease-out read as responsive; at 2s with a linear timing they read as decorative. The [msm_tool slug="css-animation"] gives you every part of it rather than only a duration.
Animate transform and opacity
This is the performance rule that actually matters, and it is short.
Changing transform or opacity can be handled by the compositor without recalculating layout or repainting. Changing width, height, top, left, margin or padding forces the browser to redo layout on every frame, for every element affected.
The practical translation:
- Move things with
transform: translate(), not top and left.
- Resize with
transform: scale(), not width and height.
- Fade with
opacity, not by changing a colour's alpha.
- Rotate with
transform: rotate(), which has no layout equivalent anyway.
An animation that stutters is almost always animating a layout property. Switching it to a transform usually fixes it outright.
Fill mode decides what happens at the end
By default an element snaps back to its unanimated state the moment the animation finishes. For a looping animation that is invisible. For a one-shot entrance it is the bug people report as "my animation runs and then everything jumps".
forwards holds the final keyframe, which is what an entrance almost always wants. backwards applies the first keyframe during any delay, which stops an element flashing at its natural position before the animation starts. both does each.
If you set a delay and see a flash before the movement begins, backwards is the fix.
Direction, and avoiding the jump
A looping animation whose last keyframe differs from its first will snap back each time round. alternate runs it forwards then backwards instead, so the loop is seamless without writing a return journey into the keyframes.
That halves the keyframe work for anything that pulses, breathes or sways, and it is why those effects usually need only two keyframes rather than three.
The easing applies between each pair of keyframes
Not across the whole sequence. This one catches people out.
Adding ease-in-out to a four-keyframe animation eases four separate times, producing a visible stutter at each keyframe rather than one smooth arc. If you want a single curve across the whole thing, use two keyframes and let the timing function do the shaping. If you genuinely need intermediate positions, consider linear between them so the steps do not fight each other.
Infinite motion is tiring
A permanently animating element competes with everything else on the page for attention, and it does not stop competing. Reserve infinite loops for genuine loading states, where the movement is telling the user something is happening.
A pulsing call-to-action button is the classic misuse. It draws the eye once and irritates thereafter, and on a page someone uses daily it becomes a reason to leave.
Reduced motion is not optional courtesy
Vestibular disorders make large or repeated motion genuinely unpleasant — nausea and dizziness rather than mild annoyance. Operating systems expose a setting for it, browsers pass it through, and honouring it costs three lines:
@media (prefers-reduced-motion: reduce) { .animated { animation: none; } }
The generated CSS includes that block. A more considered approach is to reduce rather than remove — keep a fade, drop the movement — since an interface with no transitions at all can be harder to follow. Either is better than ignoring the request.
Animation or transition
A transition runs between two states when something changes: a hover, a class being added, a value updating. It needs a trigger and it goes one way and back.
An animation runs on its own through keyframes, can loop, and can have as many intermediate steps as you like. If the movement has a start and an end tied to an interaction, it is a transition. If it runs by itself or needs more than two points, it is an animation.