CSS Transitions and Animations in Practice: From transition to Scroll-Driven
Motion is not "icing on the cake" — it is part of interface feedback and brand feel. CSS transitions and @keyframes animations let developers create smooth motion with a few lines of declarative code instead of JS frame-by-frame manipulation. As MDN points out, CSS animations beat script-driven animation in three ways: no JavaScript required, they run well even under moderate load, and the browser can skip frame updates for invisible tabs.
This is practical content in the frontend building category. For deeper animation performance work, see the web animation performance optimization; for the accessibility angle on "reducing motion", see the accessibility guide.
Transition: Smoothing State Changes
A transition is an implicit "A to B" animation: when a property changes, the browser fills in the intermediate frames. The transition shorthand configures four factors — property duration timing-function delay:
a {
background-color: #333; color: #fff;
transition: all 0.5s ease-out;
}
a:hover, a:focus { background-color: #fff; color: #333; }
- List only the properties you animate:
transition: background-color 0.3s, transform 0.3s, avoiding the surprises ofall; - Easing functions: use
cubic-bezieror common curves from easings.net instead of the default everywhere; - The
transitionendevent: JavaScript can detect when a transition finishes (note thatdisplay: noneor changing the value mid-way cancels the event).
@keyframes: Defining Animation Sequences
The animation shorthand works together with @keyframes. from/0% is the start, to/100% is the end, and any percentage keyframes can sit in between:
p {
animation: 3s infinite alternate slide-in;
}
@keyframes slide-in {
from { translate: 150vw 0; scale: 200% 1; }
to { translate: 0 0; scale: 100% 1; }
}
Useful sub-properties: animation-duration, animation-iteration-count (infinite repeats forever), animation-direction (alternate moves back and forth), animation-delay, and animation-fill-mode (forwards/backwards control the before/after states). Events such as animationstart/animationend/animationiteration let you coordinate with JavaScript.
Transition or Animation: How to Choose
The two have different positioning: transition suits "switching between state A and state B" (hover, selected, expand/collapse), triggered by a state change; animation suits "motion with its own timeline, not externally triggered" (looping spinners, auto-playing marquees, sequences played on entry). The test is simple: does the motion depend on a state change? If yes, use a transition; otherwise use an animation. When mixing them, note that animation overrides transition for the same property, so avoid duplicate rules. A table makes the difference easier to remember:
| Dimension | transition | animation |
|---|---|---|
| Trigger | State change | Auto-played, own timeline |
| Keyframes | Only two states | from/to plus any mid frames |
| Looping | No | Yes, infinite |
| Pause/reverse | No | Controlled via direction |
| Typical use | Hover, expand, selected | Loading, carousel, entry sequences |
Enter and Exit Animations: Even display Can Animate
Fading an element in/out while removing it used to be painful because display: none could not be transitioned. Modern browsers now support discrete animation of display and content-visibility: combined with transition-behavior: allow-discrete and @starting-style, elements can transition smoothly on first appearance (see MDN's Using CSS transitions):
div {
display: none; opacity: 0;
transition: opacity 1s, display 1s allow-discrete;
}
div.showing {
opacity: 1; display: block;
}
@starting-style { div.showing { opacity: 0; } }
Note that when transitioning from display: none to a visible value, the browser flips to visible at 0% so the fade-in is visible throughout; conversely it only hides at 100%.
A Complete Example: Modal Enter/Exit
Combine the techniques above into a modal that needs no hand-written JS frame animation:
.dialog {
opacity: 0;
transform: translateY(12px);
transition: opacity .25s ease, transform .25s ease,
display .25s allow-discrete;
}
.dialog.open {
opacity: 1;
transform: none;
display: block;
}
@starting-style { .dialog.open { opacity: 0; } }
Paired with aria-hidden and focus management, the modal transitions smoothly both ways: on close, the browser only removes the element from layout after the transition ends, avoiding a jarring "pop-out"; on first open, @starting-style supplies the start state. For a high-frequency interaction like modals, this snippet is "feedback first" in concrete form.
Scroll-Driven Animations: Scroll as a Timeline
Scroll-driven animations (see web.dev) map scroll progress to animation progress. The core is animation-timeline:
.progress-bar {
animation: grow linear both;
animation-timeline: scroll(); /* follows page scroll */
}
@keyframes grow { from { width: 0 } to { width: 100% } }
scroll(): based on the scroll progress of the scrolling container;view(): based on when the element enters the viewport — ideal for "reveal on scroll into view" effects;- Add
view-timeline-nameandanimation-rangeto fine-tune the start and end range.
Such effects are naturally in sync with scroll, need no JS scroll listeners, perform better, and make "scroll to this point, progress to that point" narrative pages easy to build.
Performance Essentials
- Animate compositor-friendly properties: prefer
transformandopacity; avoid animatingwidth/height/topwhich trigger layout and paint; - Use
will-changesparingly: declare it only when an animation is actually in progress, to avoid permanently reserving GPU memory; - Respect
prefers-reduced-motion: provide a reduced-motion fallback for users who request it — this is a basic accessibility requirement; - Control the animation count: too many simultaneous animations hurt frame rate; decorative motion on long pages should be restrained.
Writing the reduced-motion fallback is simple — one media query covers the whole page:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
This compresses every animation and transition to near-instant: elements still appear and disappear, they just do not move — which respects users who are sensitive to motion without breaking content usability.
16IDC Takeaway
The highest-value motion is "feedback first": button hovers, form validation, modal enter/exit. The smoothness of these high-frequency interactions improves real experience more than a big hero animation. Package transitions, enter/exit and scroll-driven patterns into reusable classes (combined with the Tailwind utility template) to speed up page development. Always keep fallbacks for low-end devices and users who prefer reduced motion.
Reference: MDN Using CSS animations https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animations/Using_CSS_animations · MDN Using CSS transitions https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_transitions/Using_CSS_transitions · web.dev scroll-driven animations https://web.dev/articles/scroll-driven-animations
Source: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animations/Using_CSS_animations