Transitions and Animations Without Overdoing It
· 4 min read
Every lesson so far has been about static appearance: how things look at a single moment.
Every lesson so far has been about static appearance: how things look at a single moment. This lesson is about movement, specifically how to make changes between states feel smooth rather than instant and jarring, using transitions and animations. Used well, motion makes an interface feel polished and responsive to the user. Used badly, it makes a site feel slow, distracting, or even nauseating. This lesson focuses as much on restraint as on the mechanics.
Basic Css Transitions
A transition tells the browser to smoothly animate a property change over time, instead of jumping instantly from the old value to the new one. You specify which property to animate, how long it should take, and optionally a timing curve.
.button-primary {
background-color: #2563eb;
transition: background-color 0.2s ease;
}
.button-primary:hover {
background-color: #1e40af;
}
Without the transition line, hovering over the button would snap instantly to the darker color. With it, the color smoothly fades over 0.2 seconds. That single line is responsible for the vast majority of the "smooth, polished" feeling you notice on well-built websites: buttons, links, and cards subtly transitioning on hover rather than changing abruptly.
Transitioning Multiple Properties
You can transition multiple properties at once, either by listing several, separated by commas, or using the keyword all to transition everything that changes (though all can be less predictable and slightly more expensive for the browser, so naming specific properties is usually the better habit).
.card {
transform: translateY(0);
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 20px rgba(0,0,0,0.15);
}
This is the classic "card lifts slightly on hover" effect found across countless modern websites, and it's built from exactly two properties changing together smoothly: a small upward shift using transform: translateY, and a deeper shadow to reinforce the sense that the card has physically lifted off the page.
Notice the use of transform rather than animating top or margin to move the card. This isn't arbitrary: transform (and opacity) can usually be animated very cheaply by the browser, often using the GPU, without forcing the browser to recalculate the layout of surrounding elements. Animating properties like width, height, top, or margin can trigger expensive layout recalculations on every single frame, which can make animations feel janky, especially on slower devices. A practical rule: prefer animating transform and opacity whenever possible; be cautious animating anything that affects layout.
Keyframe Animations Explained
Transitions only handle a change between two states (like normal and hover). For more complex, multi-step motion that plays on its own, you use @keyframes combined with the animation property.
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.notification-dot {
width: 10px;
height: 10px;
background: crimson;
border-radius: 50%;
animation: pulse 1.5s ease-in-out infinite;
}
The @keyframes block describes the stages of the animation as percentages of its total duration, and animation: pulse 1.5s ease-in-out infinite plays that named sequence repeatedly, forever, at 1.5 seconds per cycle. This kind of subtle pulsing dot is a common technique for drawing a small amount of attention to a notification icon without being obnoxious about it.
Timing functions (ease, ease-in, ease-out, ease-in-out, linear) control the pacing of the motion, not just its speed. linear moves at a constant rate the whole time, which often feels mechanical and slightly unnatural for UI motion. ease-out starts fast and slows down toward the end, which usually feels the most natural for elements entering the screen (like a dropdown appearing), because it mimics how physical objects decelerate.
Using Motion With Restraint
Now, the restraint half of this lesson. A common beginner mistake, once they discover animation, is adding motion everywhere: bouncing buttons, spinning icons, sliding everything in from off-screen on page load. This quickly becomes exhausting and unprofessional rather than impressive. A good guideline used by experienced designers: motion should have a clear purpose, confirming an action happened, drawing attention to one specific important change, or softening an otherwise abrupt state change, not decoration for its own sake.
Another real consideration is accessibility. Some users experience discomfort or even physical symptoms from excessive motion, and browsers expose a setting for this that CSS can respect directly.
@media (prefers-reduced-motion: reduce) {
* {
animation: none !important;
transition: none !important;
}
}
This respects a user's operating system preference to minimize motion, and professional sites increasingly include something like this as a baseline courtesy, not an edge case.
Practical tip: when adding a transition or animation, ask whether removing it would make the interface feel noticeably worse. If the answer is "not really," it's probably unnecessary decoration rather than purposeful motion.
Staggering With Transition Delay
One more small but genuinely useful detail: the transition-delay property lets you stagger when a transition starts, which is handy for sequencing several elements so they don't all move at exactly the same instant.
.card:nth-child(1) { transition-delay: 0s; }
.card:nth-child(2) { transition-delay: 0.05s; }
.card:nth-child(3) { transition-delay: 0.1s; }
Applied to a row of cards that fade or lift in together, this tiny stagger makes the motion feel more natural and less mechanical, since almost nothing in the real world moves perfectly in unison.
A quick exercise: build a button for a profile card labeled "Suit up, Tony Stark," and give it a smooth background-color transition on hover, plus a subtle translateY lift using transform. Then add a small pulsing notification dot using @keyframes, and finally add a prefers-reduced-motion media query that disables both.
KEEP READING