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 of all;
  • Easing functions: use cubic-bezier or common curves from easings.net instead of the default everywhere;
  • The transitionend event: JavaScript can detect when a transition finishes (note that display: none or 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-name and animation-range to 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 transform and opacity; avoid animating width/height/top which trigger layout and paint;
  • Use will-change sparingly: 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