CSS Animation Basics: Keyframes, Transitions and When to Use Which
A clear, example-first guide to CSS animation: transitions vs keyframes, the properties that matter, how to loop, and how to keep everything smooth at 60fps.
- →A CSS transition animates between two states (like default and :hover); a keyframe animation runs a defined sequence on its own.
- →Use transitions for simple state changes and keyframes (@keyframes) for loops or multi-step motion.
- →Animate transform and opacity for smooth 60fps motion; avoid animating width, height, top and left.
- →animation-fill-mode: forwards keeps the element at its final frame after the animation ends.
- →Add prefers-reduced-motion support so users who dislike motion get a calm experience.
CSS animation is how you add movement to a web page using only CSS, no JavaScript required. Done well, it makes an interface feel polished and intentional. Done badly, it feels janky and cheap. This guide covers the two ways CSS animates things, the properties that actually matter, and the small rules that keep motion smooth.
Everything here is copy-paste ready. By the end you will know when to reach for a transition, when to reach for keyframes, and how to avoid the jank that ruins most first attempts.
The two kinds of CSS animation
There are exactly two tools. Almost every effect you see on the web is one of these:
| Tool | What it does | Best for |
|---|---|---|
| transition | Animates between two states when a value changes | Hover, focus, toggles, show/hide |
| @keyframes + animation | Runs a defined multi-step sequence on its own | Loops, entrances, loaders, attention |
A transition needs a trigger (usually :hover or a class change). A keyframe animation can start on its own, repeat forever, and move through many steps. That single difference tells you which one to use.
CSS transitions: animating a state change
A transition says: when this property changes, don't jump, glide. You define which property to watch, how long it takes, and the easing. Here is a button that smoothly lifts on hover:
.button {
transform: translateY(0);
transition: transform .25s ease, box-shadow .25s ease;
}
.button:hover {
transform: translateY(-3px);
box-shadow: 0 10px 24px rgba(0,0,0,.25);
}The shorthand order is property, duration, timing-function, delay. You can transition multiple properties by separating them with commas, as above. Never use transition: all in production, it watches every property and can animate things you didn't intend, hurting performance.
CSS keyframes: animating a sequence
What is keyframes in CSS animation? @keyframes defines the steps of an animation, from 0% to 100% (or from and to). You then attach it to an element with the animation property. This runs on its own, no hover needed, which is why it is used for loaders, entrances and looping effects.
@keyframes float {
0% { transform: translateY(0); }
50% { transform: translateY(-10px); }
100% { transform: translateY(0); }
}
.badge {
animation: float 2s ease-in-out infinite;
}That gently floats an element up and down forever. The animation shorthand is: name, duration, timing-function, delay, iteration-count, direction, fill-mode.
The animation properties worth knowing
- •animation-duration: how long one cycle takes (2s, 400ms).
- •animation-timing-function: the easing curve (ease, linear, cubic-bezier(...)).
- •animation-delay: wait before it starts.
- •animation-iteration-count: a number, or infinite to loop.
- •animation-direction: normal, reverse, or alternate (plays back and forth).
- •animation-fill-mode: forwards keeps the last frame; backwards applies the first frame during the delay.
How to loop a CSS animation: set animation-iteration-count: infinite. To make it go back and forth smoothly instead of jumping, add animation-direction: alternate.
Transition vs animation: which should you use?
CSS animation vs transition is the question everyone hits. The simple rule: if the motion happens because of a user action and moves between two states, use a transition. If the motion needs to run by itself, repeat, or pass through several steps, use a keyframe animation.
- 1.Hover color, lift, or fade → transition.
- 2.Loading spinner → keyframes with infinite.
- 3.Element sliding in on page load → keyframes (or a JS library for scroll triggers).
- 4.Accordion open/close → transition on height or a transform.
- 5.Pulsing 'live' dot → keyframes with alternate.
The one rule that keeps animation smooth
Animate transform and opacity. That's the whole secret to 60fps. These two properties can be handled by the GPU without recalculating the page layout, so they stay smooth even on cheap phones. Animating layout properties forces the browser to re-measure the page on every frame, which causes jank.
| Cheap (smooth) | Expensive (janky) |
|---|---|
| transform: translate / scale / rotate | top, left, right, bottom |
| opacity | width, height, margin, padding |
| filter (sparingly) | box-shadow on large elements |
If your animation feels laggy, you're almost always animating the wrong property. Swap width/left for transform and it usually fixes itself.
Practical examples
Fade-and-rise entrance for cards
A clean way to animate cards in (css animation for cards). Combine opacity and a small translateY, and stagger the delay per card:
@keyframes rise {
from { opacity: 0; transform: translateY(16px); }
to { opacity: 1; transform: translateY(0); }
}
.card { animation: rise .5s ease-out both; }
.card:nth-child(2) { animation-delay: .08s; }
.card:nth-child(3) { animation-delay: .16s; }A shimmer/loading placeholder
@keyframes shimmer {
100% { transform: translateX(100%); }
}
.skeleton { position: relative; overflow: hidden; background: #1a1d27; }
.skeleton::after {
content: ""; position: absolute; inset: 0;
background: linear-gradient(90deg, transparent, rgba(255,255,255,.08), transparent);
transform: translateX(-100%);
animation: shimmer 1.4s infinite;
}Respect users who dislike motion
Some people get motion sickness or simply prefer calm interfaces, and browsers expose that preference. Always honor it, it is an accessibility must and a quiet quality signal.
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: .01ms !important;
animation-iteration-count: 1 !important;
transition-duration: .01ms !important;
}
}The short version
Use transitions for state changes, keyframes for sequences and loops, and animate only transform and opacity to stay smooth. Add prefers-reduced-motion, and you have covered the fundamentals of CSS animation. For scroll-triggered and physics-based motion you'll want JavaScript, which is a separate topic.
You can find ready-made animated components built on exactly these principles, from particle fields to animated buttons and loaders, in my UI library. Copy the code and ship.
Frequently asked questions
What is the difference between a CSS transition and a CSS animation?+
A transition animates between two states when a value changes, such as from default to :hover, and needs a trigger. A CSS animation uses @keyframes to run a defined multi-step sequence on its own, can loop, and doesn't need a trigger. Use transitions for simple state changes and keyframes for loops or entrances.
What are keyframes in CSS animation?+
@keyframes define the steps of an animation from 0% to 100% (or from and to). You describe how the element should look at each point, then attach the sequence to an element with the animation property. This lets an element animate on its own through multiple steps.
How do I loop a CSS animation?+
Set animation-iteration-count: infinite on the element. To make it play back and forth smoothly instead of restarting from the beginning each time, also add animation-direction: alternate.
Which CSS properties are best to animate for performance?+
Animate transform (translate, scale, rotate) and opacity. These are GPU-accelerated and don't trigger layout recalculation, so they stay smooth at 60fps. Avoid animating width, height, top, left, margin and padding, which force expensive layout work each frame.
Can I do CSS animation without JavaScript?+
Yes. Transitions and @keyframes animations are pure CSS and need no JavaScript. You only need JavaScript for advanced cases like scroll-triggered animations, drag interactions, or physics-based motion, where a library such as GSAP helps.
I build fast, SEO-ready sites and rank them on Google and AI search.