Motion on the web is not just aesthetic—it is functional. It provides feedback and guides the user's attention. There are two native interpolation engines: Transitions (for simple state changes) and Keyframe Animations (for complex visual storytelling).
1What are transitions and how do they work?
Transitions are the most basic interpolation engine. They require a 'trigger' such as a :hover state or a class added by JS. The browser automatically calculates the intermediate frames between the initial and final values based on a mathematical acceleration curve (timing-function).
Comparison: Transition vs Animation
| Feature | Transition | Animation (@keyframes) | Ideal Use Case |
|---|---|---|---|
| Trigger | Requires interaction (e.g., hover) | Runs automatically or on load | Interactive feedback vs Atmosphere |
| Complexity | 2 States (Start and End) | Multiple steps (0% to 100%) | Buttons and menus vs Loaders and cinematics |
| Loop| Runs once | Can be infinite (infinite) | UI states vs Loading elements |
2Why avoid animating margins or widths (Layout Thrashing)?
The golden rule of web performance: Never animate properties that alter DOM geometry (such as width, height, margin, top, left). Doing so forces the browser engine to recalculate the positions of all other elements on every frame—a computationally destructive process called *Layout Thrashing*.
To guarantee 60 frames per second (fps), strictly animate transform and opacity. These properties are offloaded from the main thread and handled by the graphics card (GPU) compositor. For extreme cases on mobile, using will-change: transform pre-creates a layer in the GPU compositor, ensuring lag-free execution.
3Step-by-Step Breakdown
Motion & Animation Engine. Static pages are boring. Today, we master CSS Transitions and Animations—the engine that adds life, motion, and hardware-accelerated smoothness to your user interfaces.
State Changes: transition. Transitions smoothly interpolate property changes from an initial state (A) to a new state (B). They require a trigger, usually a user interaction like :hover. You define the property, the duration, and the timing function.
Which CSS feature is explicitly designed for simple, automatic interpolation between two states (e.g., normal and hover) without requiring a multi-step timeline?
- →transition
- →animation
Complex Timelines: @keyframes. While transitions go from A to B, animations are multi-step stories. Use the @keyframes rule to define the state of an element at specific percentages of time (from 0% to 100%). These can run automatically without user interaction.
Which keyword within the animation shorthand causes an animation to play continuously in a loop forever?
- →loop
- →infinite
Timing Functions: cubic-bezier. Timing is everything. linear movement feels robotic. ease-out feels natural. For professional, premium motion, use a cubic-bezier curve to create unique, snappy, elastic bouncy effects.
In the shorthand transition: opacity 0.5s ease-in;, what specific characteristic of the animation does 'ease-in' define?
- →timing-function
- →delay
Performance: GPU vs CPU. Never animate properties that alter geometry like width, height, or margin. This triggers 'Layout Thrashing', forcing the CPU to redraw the DOM constantly. Instead, animate transform and opacity. These are handled by the GPU, ensuring 60 frames per second (fps).
To ensure fluid animations without causing heavy DOM reflows (Layout Thrashing), which CSS property is vastly superior for moving an element horizontally?
- →left
- →transform
Cascading: animation-delay. The animation-delay property pauses the start of an animation. By incrementally increasing the delay on a list of elements, you create a sophisticated cascading or staggered loading effect.
Preserving States: animation-fill-mode. By default, when a keyframe animation ends, the element violently snaps back to its original (0%) state. To prevent this and freeze the element at its final 100% state, use animation-fill-mode: forwards.
Which property value ensures an animation stays locked exactly at its final (100%) frame styles upon completion?
- →infinite
- →forwards
Motion Secured. You have mastered the Motion Engine. You know how to interpolate states with transitions, build multi-stage timelines with keyframes, and prioritize GPU acceleration to prevent layout thrashing. Your interfaces are now alive.
Animate A Property Change Smoothly. transition-duration controls how long a property change animates instead of happening instantly.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Always Respect prefers-reduced-motion for Infinite or Large-Scale Animations
Infinite pulsing, parallax, or large-motion `@keyframes` animations can trigger dizziness, nausea, or migraines in users with vestibular disorders. Wrap them in `@media (prefers-reduced-motion: reduce)` and either disable the animation or replace it with a simple opacity fade.
@media (prefers-reduced-motion: reduce) {
* { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; }
}2Animated Focus Indicators Must Still Be Clearly Visible at Every Frame
If a focus ring is animated (e.g., fading in over 0.3s), keyboard users tabbing quickly through a form may never see it at full opacity before moving to the next field. Focus-state transitions should be near-instant or skipped entirely, unlike purely decorative hover transitions.
SEO Implications
- 1
Animating transform/opacity Instead of Layout Properties Protects Core Web Vitals
Animating `width`, `height`, `top`, or `margin` triggers layout recalculation on every frame, which can register as Cumulative Layout Shift or hurt Interaction to Next Paint; animating `transform` and `opacity` is handled by the compositor and avoids this layout cost entirely.
- 2
Autoplaying, Large-Motion Animations Above the Fold Can Distract From Content Crawlers Weigh for Relevance
While animations themselves aren't directly indexed, an aggressively animated hero section that delays perceived content stability can indirectly affect engagement signals and Cumulative Layout Shift if the animated element changes the page's layout dimensions during its motion.
Best Practices
Default Every New Animation to Respecting prefers-reduced-motion
Rather than retrofitting accessibility after the fact, wrap decorative `animation` and `transition` declarations in a reduced-motion media query check from the start of a project, so vestibular-safe behavior is the default rather than an afterthought.
Reserve animation for Autonomous or Multi-Step Sequences, transition for Simple State Changes
Using `@keyframes` for a simple two-state hover effect (like a color change) adds unnecessary complexity; save `animation` for looping, multi-stage, or auto-playing sequences, and use lightweight `transition` for anything triggered by a direct state change like `:hover` or `:focus`.
Frequent Bugs
A `@keyframes` animation visually 'snaps back' to its starting state the instant it finishes.
By default, an animation's styles don't persist after its final iteration completes. Add `animation-fill-mode: forwards` so the element retains the styling defined in the last keyframe instead of reverting.
An animated element causes visible jank or stutter on scroll, especially on mobile.
The animation is likely touching a layout-triggering property like `width`, `top`, or `margin-left` instead of `transform`. Rewrite the keyframes to use `transform: translate/scale/rotate` and `opacity`, which the browser can composite on the GPU without recalculating layout each frame.
Real-World Examples
Building a Staggered List Entrance Animation That Respects Motion Preferences
A dashboard's notification list used staggered `animation-delay` values so items appeared to cascade in one after another, but the effect needed to gracefully degrade to an instant, non-jarring appearance for users with motion sensitivity enabled.
@keyframes popIn {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
.list-item { animation: popIn 0.4s ease-out both; }
.list-item:nth-child(2) { animation-delay: 0.08s; }
.list-item:nth-child(3) { animation-delay: 0.16s; }
@media (prefers-reduced-motion: reduce) {
.list-item { animation: none; }
}