CSS Transitions vs CSS Animations
CSS has two systems for movement: transitions and animations. Transitions respond to state changes (hover, focus, class toggle) — they smoothly interpolate a property from its current value to a new one when triggered. Animations use @keyframes to define a sequence of states that play automatically, loop, or reverse without needing a state change trigger.
Use transitions for simple interactive feedback. Use animations for continuous motion, multi-step sequences, or effects that play on load.
@keyframes Syntax
@keyframes slide-in {
from {
transform: translateX(-100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}You can use from/to for two-step animations or percentage stops for more complex sequences:
@keyframes bounce {
0% { transform: translateY(0); }
40% { transform: translateY(-30px); }
60% { transform: translateY(-15px); }
80% { transform: translateY(-5px); }
100% { transform: translateY(0); }
}Applying an Animation
.element {
animation-name: slide-in;
animation-duration: 0.4s;
animation-timing-function: ease-out;
animation-delay: 0.1s;
animation-iteration-count: 1;
animation-fill-mode: both;
}Or using the shorthand: animation: slide-in 0.4s ease-out 0.1s 1 both;
Timing Functions
The timing function controls how the animation progresses over its duration:
- ease (default): slow start, fast middle, slow end
- ease-in: starts slow, ends fast — good for elements leaving the screen
- ease-out: starts fast, ends slow — good for elements entering the screen
- ease-in-out: slow at both ends
- linear: constant speed — good for loops like spinners
- cubic-bezier(x1, y1, x2, y2): fully custom curve
spring-like physics can be approximated with a cubic-bezier that overshoots: cubic-bezier(0.34, 1.56, 0.64, 1) creates a slight bounce at the end of an entrance animation.
fill-mode
animation-fill-mode determines what styles apply outside the animation's active period:
- none: element returns to its original style before and after
- forwards: element keeps the styles of the last keyframe after the animation ends
- backwards: element has the styles of the first keyframe during the delay period
- both: combines forwards and backwards
Performance: The Compositor Thread
CSS animations perform best when they only animate properties the browser can handle on the compositor thread — without involving layout or paint. These are: transform (translate, rotate, scale) and opacity. Animating these properties doesn't trigger a layout recalculation, so they run smoothly even under CPU load.
Animating width, height, top, left, or background-color triggers layout or paint, which is much more expensive and causes jank on complex pages.
For any animation involving movement or size change, use transform: translate() or transform: scale() rather than changing positional or dimensional properties directly.
Adding will-change
will-change: transform hints to the browser that an element will be animated, promoting it to its own GPU layer in advance. Use it sparingly — on the specific elements that animate, not globally. Overusing will-change consumes extra GPU memory.
Accessibility: prefers-reduced-motion
Some users experience motion sickness or seizures from animation. Respect their preferences:
@media (prefers-reduced-motion: reduce) {
.animated-element {
animation: none;
transition: none;
}
}This media query detects when the user has enabled "Reduce motion" in their OS accessibility settings and disables animations accordingly.