DeepFrontend
Practice ArenaBlogSign in
Go Pro
DeepFrontend

Interview-grade learning paths and hands-on practice for mid-to-senior engineers. Master internals, patterns, and system architecture with runnable code.

All systems operational

Core Courses

  • -JavaScript Internals
  • -React Reconciliation
  • -TypeScript rigor
  • -Next.js Caching
  • -Node.js Event Loop
  • -System Design
  • -Web Security
  • -CSS & Page Layouts

Explore

  • Learning Paths
  • Practice Arena
  • Pricing Options
  • HTML Sitemap

Resources

  • Privacy Policy
  • Terms of Service
  • Email Support

© 2026 DeepFrontend. All rights reserved.

Expert learning environments for web engineering teams.

Home/CSS/Transitions
beginner15 min read·Updated Jul 2026

Transitions

CSS transitions animate a property change between two states, triggered by something else (hover, class toggle, focus) — the simplest motion primitive, and the right tool for the vast majority of UI micro-interactions.

csstransitionsanimationmotion

Knowledge Check

4 questions · pass at 70%

0/4
easymcq

1. What is the key difference between a CSS transition and a CSS @keyframes animation?


Interview Questions

3 questions

Cheatsheet

Download-ready reference
Cheatsheet
transition: property duration timing-function delay;
  e.g. transition: transform 0.2s ease-out, opacity 0.2s ease-out;

Timing functions:
  linear        constant speed — rarely right for UI, feels robotic
  ease          default: slow-fast-slow
  ease-out      fast start, slow end — good for elements appearing/responding
  ease-in       slow start, fast end — good for elements leaving
  ease-in-out   slow both ends — good for in-place movement
  cubic-bezier(x1,y1,x2,y2)  custom curve

PERFORMANCE — the single most important rule:
  transform + opacity  → GPU compositor thread only, cheap, no layout recalc
  width/height/top/left/margin → triggers LAYOUT every frame → janky on real devices
  Fix: use transform: translate()/scale()/rotate() instead of positional properties

Can't transition to/from `auto`:
  grid-template-rows: 0fr → 1fr   (modern CSS-only fix, handles dynamic content)
  OR measure scrollHeight via JS, transition to that pixel value

transitionend event: element.addEventListener('transitionend', (e) => { e.propertyName })

Accessibility: @media (prefers-reduced-motion: reduce) { /* shorten/disable */ }

Avoid: transition: all;  → list specific properties instead

Related topics

FlexboxAnimationsProCSS PerformancePro

On this page

15m
Knowledge CheckInterview QuestionsCheatsheet

Concept#

A CSS transition animates a property smoothly between its old and new value whenever that value changes — triggered externally (a :hover, a class toggle from JS, a :focus state), not self-initiated. This is the key distinction from @keyframes animations (covered in the next topic), which run independently and can loop, have multiple stages, and start without an external trigger.

The four transition properties#

.button {
  transition-property: background-color, transform;
  transition-duration: 0.2s;
  transition-timing-function: ease-out;
  transition-delay: 0s;
 
  /* shorthand — what you'll write almost always */
  transition: background-color 0.2s ease-out, transform 0.2s ease-out;
}
 
.button:hover {
  background-color: #4338ca;
  transform: translateY(-2px);
}
  • transition-property — which properties to animate (all works but is a performance and specificity footgun, covered below).
  • transition-duration — how long the transition takes.
  • transition-timing-function — the easing curve (covered below).
  • transition-delay — wait time before starting.

Timing functions — the difference between motion feeling natural or robotic#

transition-timing-function: linear;       /* constant speed — feels mechanical, rarely what you want for UI */
transition-timing-function: ease;         /* default — slow start, fast middle, slow end */
transition-timing-function: ease-in;      /* slow start, fast end — good for elements leaving */
transition-timing-function: ease-out;     /* fast start, slow end — good for elements entering/appearing */
transition-timing-function: ease-in-out;  /* slow start AND end — good for elements moving in place */
transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); /* custom curve, e.g. for a "bounce" overshoot */

Real UI motion almost never uses linear — physical objects don't move at constant velocity, they accelerate and decelerate, and linear motion reads as distinctly artificial/robotic to the eye even when the difference is subtle. ease-out is generally the best default for elements appearing/responding to interaction (a button press, a tooltip appearing) since it front-loads the perceived responsiveness.

transition: all — convenient, but a real cost#

/* Convenient but transitions EVERY animatable property that changes, including ones you didn't intend */
.card { transition: all 0.2s; }

all is tempting shorthand but has two real costs: it animates properties you didn't intend to (a layout shift caused by something unrelated now animates too, potentially looking like a bug), and it's more expensive for the browser to watch every property for changes rather than a specific known list. List the specific properties you actually intend to animate.

Only transform and opacity animate on the compositor thread#

/* Cheap — GPU compositor thread, no layout/paint recalculation needed */
.card { transition: transform 0.2s, opacity 0.2s; }
.card:hover { transform: scale(1.05); opacity: 0.9; }
 
/* Expensive — triggers layout recalculation on every frame of the transition */
.card { transition: width 0.2s, top 0.2s; }
.card:hover

This is the single most important performance fact about CSS animation: transform and opacity are the only properties that can be animated purely on the GPU compositor thread, without triggering layout (reflow) or paint on every frame. Animating width, height, top/left, margin, or similar box-model/positional properties forces the browser to recompute layout on every single frame of the transition — on complex pages or lower-end devices, this is the direct cause of janky, dropped-frame animations. Wherever possible, express motion in terms of transform: translate()/scale()/rotate() instead of animating layout-affecting properties directly (e.g. transform: translateX(20px) instead of animating left).

Detecting when a transition finishes#

element.addEventListener("transitionend", (e) => {
  console.log(`${e.propertyName} finished transitioning`);
});

Useful for cleanup after a transition completes — removing an element from the DOM only after its fade-out transition finishes, for instance, rather than yanking it out instantly while still visually mid-animation.

Respecting prefers-reduced-motion#

@media (prefers-reduced-motion: reduce) {
  * {
    transition-duration: 0.01ms !important;
  }
}

Covered in depth in the Accessibility topic — some users have vestibular disorders where motion genuinely causes physical discomfort, not just an aesthetic dislike. Respecting this media query is a real accessibility requirement, not an optional nicety.


Common Mistakes#

1. Using transition: all by default#

Covered above — animates unintended property changes and is measurably more expensive than listing specific properties.

2. Animating width/height/top/left/margin instead of transform#

/* Janky on complex pages — triggers layout every frame */
.menu { left: -300px; transition: left 0.3s; }
.menu.open { left: 0; }
 
/* Smooth — GPU compositor only */
.menu { transform: translateX(-300px); transition: transform 0.3s; }
.menu.open { 

This single substitution (left/top → transform: translate()) is the most impactful CSS animation performance fix available, and it's almost always a drop-in replacement for the common case of sliding/moving an element.

3. Forgetting the transition only applies to the property change, not the initial render#

/* Element appears already in its "hover" state on first paint — no transition plays,
   because there was no PRIOR value to transition FROM on initial render */

Transitions only animate a change from one value to another. If JS adds a class with the target styles at the same moment the element is inserted into the DOM (no separate paint in between), the browser may not register a "from" state to animate from, and the element just appears instantly in its final state. This is why entrance animations sometimes require a requestAnimationFrame delay or a brief timeout between insertion and class toggle.

4. Not respecting prefers-reduced-motion#

Especially relevant for larger, more dramatic transitions (page transitions, big layout shifts) — smaller UI micro-interactions are less commonly an issue, but anything with real motion scale should respect the preference.

5. Transitioning height: auto and expecting it to animate smoothly#

/* Doesn't work as expected — you can't transition TO/FROM `auto`, only between two explicit values */
.accordion { height: 0; transition: height 0.3s; }
.accordion.open { height: auto; }

CSS transitions require two concrete values to interpolate between — auto isn't a fixed value the browser can animate toward smoothly (the common workaround is transitioning max-height to a large-enough fixed value, or using grid-template-rows: 0fr → 1fr which does animate smoothly as of modern browser support, or measuring the actual content height via JS and transitioning to that specific pixel value).


Best Practices#

  • List specific properties in transition, not all.
  • Prefer transform/opacity for any animation where a layout-affecting alternative exists — this is the highest-leverage CSS animation performance practice.
  • Use ease-out as a sensible default for interactive/appearing elements; reserve linear for cases where constant velocity is genuinely correct (some loading indicators).
  • Respect prefers-reduced-motion, especially for anything beyond small micro-interactions.
  • Use the trick (or JS-measured height) for animating to/from height, since transitioning directly to/from doesn't work.

Further Resources#

  • MDN — CSS transitions
  • MDN — transition-timing-function
  • web.dev — Animations Guide (compositor-only properties)
  • cubic-bezier.com — visual easing curve editor.
  • CSS-Tricks — Using CSS Transitions on Auto Dimensions

{
width
:
300
px
;
top
:
10
px
; }
transform
:
translateX
(
0
); }
grid-template-rows: 0fr → 1fr
auto
auto
  • Test on a throttled/lower-end device profile, not just a high-end development machine — janky transitions are far more apparent on real-world hardware.