Guide 6 of 6 · Web
Animating for the Web with CSS: Transitions and @keyframes
Transitions, @keyframes, timing functions, performance and accessibility, with code you can paste.
Browsers can animate almost anything you can style, with no plugins or libraries. CSS gives you two tools: transitions, for moving between two states, and @keyframes animations, for anything more elaborate. Both rely on the ideas from the rest of these guides: keyframes, in-betweens, easing and timing. The browser does the in-betweening.
Transitions: two states
A transition animates a property when its value changes. You declare which property, how long and which easing, and the browser fills in the frames.
.button {
background: #2c5aa0;
transform: translateY(0);
transition: transform 180ms ease-out, background-color 180ms ease-out;
}
.button:hover {
background: #1f4683;
transform: translateY(-2px);
}
When the pointer enters, the button lifts two pixels and darkens over 180 milliseconds. When it leaves, it eases back. Transitions only run when something changes the value: a hover, focus, a class added by JavaScript. They can't loop, and they only know a start and an end.
@keyframes: many steps
For more control, define named keyframes and attach them with the animation property.
@keyframes pop-in {
0% { opacity: 0; transform: scale(0.6); }
70% { opacity: 1; transform: scale(1.06); }
100% { opacity: 1; transform: scale(1); }
}
.card {
animation: pop-in 320ms ease-out both;
}
Each percentage is a keyframe. Here the card fades in while growing slightly past full size, then settles: a small overshoot that makes the entrance feel lively. The keyframe playground on the home page generates blocks in this format.
The animation properties
The animation shorthand combines several properties:
| Property | What it does | Example |
|---|---|---|
animation-name |
Which @keyframes to use | pop-in |
animation-duration |
Length of one cycle | 320ms, 1.6s |
animation-timing-function |
Easing between keyframes | ease-out, cubic-bezier(…) |
animation-delay |
Wait before starting | 100ms |
animation-iteration-count |
How many times | 1, 3, infinite |
animation-direction |
Forward, reverse or back and forth | alternate |
animation-fill-mode |
Keep first or last keyframe styles before or after | both |
animation-play-state |
Pause or run | paused |
Fill mode trips people up. Without it, an element snaps back to its original style when the animation ends. forwards keeps the last keyframe; both also applies the first keyframe during any delay, so an element that fades in doesn't flash visible first.
Easing in CSS
CSS has keyword easings (linear, ease, ease-in, ease-out, ease-in-out), custom curves with cubic-bezier(), stepped motion with steps(), and the newer linear() function, which takes a list of points and can approximate bounces and springs.
Two details matter. First, the default is ease, not linear, so animations eased slightly even if you didn't ask. Second, in @keyframes the timing function applies to each segment between keyframes, not to the whole animation. You can set a different one inside each keyframe:
@keyframes toss {
0% { transform: translate(0, 0); animation-timing-function: ease-out; }
50% { transform: translate(150px, -120px); animation-timing-function: ease-in; }
100% { transform: translate(300px, 0); }
}
That gives a ball that slows as it rises and speeds up as it falls. The easing guide explains why.
steps() is handy for sprite sheet animation. Put the frames of a drawn animation side by side in one image, set it as a background, and animate background-position with steps(n) so it jumps from frame to frame instead of sliding.
Keep it smooth: transform and opacity
For smooth motion, animate transform (translate, scale, rotate) and opacity wherever possible. Browsers can usually handle those on the compositor, without recalculating layout or repainting. Animating width, height, top, left or margin forces layout on every frame, which can cause stutter, especially on phones.
To move something, use transform: translate() instead of left and top. To grow something, use scale() instead of width. Use will-change: transform only on elements you know will animate, and sparingly; it reserves memory for each element that has it.
Respect reduced motion
Some people get dizzy or nauseous from motion on screen, and some find it distracting. Operating systems have a setting to reduce motion, and CSS can read it:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
That's a blunt global switch. A gentler approach is to replace large movements with simple fades for people who prefer reduced motion, keeping the feedback while dropping the travel. The accessibility guidelines in WCAG also ask that motion triggered by interaction can be turned off unless it's essential, and that anything moving, blinking or scrolling for more than five seconds can be paused.
Starting animations from JavaScript
CSS animations start when their styles apply. A common pattern is to add a class with JavaScript when something should animate, such as an element scrolling into view. Listen for animationend to clean up afterward. For animations you need to pause, reverse or scrub precisely, the Web Animations API (element.animate()) uses the same keyframes and timing concepts from JavaScript.
A checklist for web motion
Keep interface animations short, usually 150 to 400 milliseconds. Use ease-out for things entering and ease-in for things leaving. Animate transform and opacity. Give every animation a purpose, such as showing where something came from or confirming an action. Respect prefers-reduced-motion. And test on a slow phone, not only on your development machine.
Questions
- Should I use a CSS transition or a CSS animation?
- Use a transition when an element changes between two states because of something like hover, focus or a class change. Use @keyframes animation when you need more than two steps, when the motion should start on its own, or when it should loop.
- Which CSS properties are cheapest to animate?
- transform and opacity. Browsers can usually animate them without recalculating layout or repainting the page. Animating width, height, top, left or margin forces layout work on every frame and can stutter on slower devices.
- What is prefers-reduced-motion?
- It's a media query that matches when someone has asked their operating system to reduce motion, often because animation makes them dizzy or distracts them. Use it to remove or tone down non-essential animation.
Further reading: MDN: Using CSS animations · W3C: Understanding WCAG 2.3.3 Animation from Interactions