Transitions in CSS

Transitions in CSS animate a property change between two states. Instead of a button instantly changing color on hover, a transition can make that change feel smooth. Transitions are commonly used for buttons, cards, menus, focus states, opacity changes, transforms, and small interface feedback.

A transition needs a start value, an end value, and a property that can be animated. CSS handles the in-between frames. This makes transitions simpler than keyframe animations when the goal is just smoothing a state change.

Basic Transition Syntax

The transition shorthand can define the property, duration, timing function, and delay.

.button {
  background: #2563eb;
  transition: background 200ms ease;
}

.button:hover {
  background: #1d4ed8;
}

When the button is hovered, the background changes over 200 milliseconds instead of switching instantly.

Transition Properties

The longhand properties are transition-property, transition-duration, transition-timing-function, and transition-delay.

.card {
  transition-property: transform, box-shadow;
  transition-duration: 220ms;
  transition-timing-function: ease;
  transition-delay: 0ms;
}

Longhand syntax is useful when you want to explain or adjust each part separately. Shorthand is shorter for production code.

transition-property

The transition-property value tells the browser which CSS properties should animate. Avoid using all by default because it can animate unexpected properties.

.card {
  transition-property: transform, opacity;
}

This is more intentional than transition: all. It also makes debugging easier because only selected properties animate.

transition-duration

The duration controls how long the transition takes. Common UI transitions are often between 150ms and 300ms. Longer durations can feel slow unless the motion is large or decorative.

.menu {
  transition-duration: 180ms;
}

Short transitions feel responsive. Slow transitions can frustrate users when they are waiting for an interface to react.

Timing Functions

The timing function controls acceleration. Common values include linear, ease, ease-in, ease-out, ease-in-out, steps(), and cubic-bezier().

.drawer {
  transition: transform 260ms cubic-bezier(0.22, 1, 0.36, 1);
}

Custom cubic-bezier values can make motion feel more natural. Use them consistently instead of inventing random curves for every component.

transition-delay

The delay controls how long the browser waits before starting the transition.

.tooltip {
  opacity: 0;
  transition: opacity 150ms ease 300ms;
}

.trigger:hover .tooltip {
  opacity: 1;
}

A delay can prevent tooltips from flashing when the pointer briefly passes over an element. Use delays carefully because they can make controls feel sluggish.

Multiple Transitions

Different properties can have different durations and timing functions.

.card {
  transition:
    transform 180ms ease,
    box-shadow 220ms ease,
    opacity 150ms linear;
}

This allows a card to lift quickly, adjust shadow smoothly, and fade at a different pace.

Animatable Properties

Not every CSS property transitions smoothly. Colors, opacity, transforms, filters, shadows, spacing, and sizes can animate. Properties like display do not transition in the normal way.

.panel {
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 180ms ease, transform 180ms ease;
}

.panel.is-open {
  opacity: 1;
  transform: translateY(0);
}

Instead of trying to transition display, transition opacity and transform, then control visibility or layout state separately when needed.

Performance Friendly Transitions

The safest properties for smooth UI transitions are usually transform and opacity. They often avoid expensive layout recalculation.

.card:hover {
  transform: translateY(-4px);
}

Animating width, height, top, left, or large shadows can be more expensive. It may still be acceptable in small cases, but use performance-friendly properties for repeated UI elements.

Transitions and Reduced Motion

Some users prefer reduced motion. Respect that preference with prefers-reduced-motion.

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

This keeps the interface usable for people who experience discomfort from motion-heavy effects.

Transitions vs Animations

Use transitions for changes between two states, such as normal to hover or closed to open. Use keyframe animations when the motion has multiple steps, loops, or should run without a state change.

NeedBetter Tool
Button hover colortransition
Dropdown open statetransition
Loading spinneranimation
Looping background movementanimation
Card hover lifttransition

State Classes and Transitions

Transitions often work with state classes added by JavaScript. CSS defines how the element moves between states, while JavaScript only toggles the class.

.modal {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 180ms ease, transform 180ms ease;
}

.modal.is-open {
  opacity: 1;
  transform: translateY(0);
}

This keeps the motion in CSS and the state management in JavaScript. It is easier to maintain than hardcoding animation details in scripts.

Transitions for Focus States

Transitions should not only be used for hover. Focus states are important for keyboard users. A focus transition can make the focus indicator feel polished while still remaining visible.

.input {
  transition: border-color 160ms ease, box-shadow 160ms ease;
}

.input:focus-visible {
  border-color: #2563eb;
  box-shadow: 0 0 0 3px #bfdbfe;
}

Never remove focus indicators without replacing them. A smooth focus state is useful only if the final state is clear.

The Height Auto Problem

Beginners often try to transition from height: 0 to height: auto. CSS cannot smoothly interpolate to auto in the usual way because auto is not a fixed numeric value.

.accordion {
  max-height: 0;
  overflow: hidden;
  transition: max-height 220ms ease;
}

.accordion.is-open {
  max-height: 500px;
}

This max-height pattern is common, but it is not perfect because the chosen maximum is a guess. JavaScript, grid tricks, or newer platform features may be better for complex accordions.

will-change

The will-change property tells the browser that a property is likely to change. It can help in specific performance-sensitive cases, but it should not be placed everywhere.

.drawer {
  will-change: transform;
}

Use will-change sparingly. Overusing it can waste memory and reduce performance instead of improving it.

Motion Design Rules

Good transitions are subtle and purposeful. They should communicate state change, direct attention, or make interaction feel responsive. They should not slow the user down.

  • Use shorter durations for small UI feedback.
  • Use longer durations only for larger movement.
  • Keep easing consistent across similar components.
  • Avoid motion that distracts from reading.
  • Respect reduced motion preferences.
  • Test transitions on actual low-end devices when possible.

Initial State Matters

A transition only happens when a property changes from one computed value to another. If the starting value is missing or set to auto, the browser may not have a clean numeric value to animate from.

.menu {
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity 180ms ease, transform 180ms ease;
}

.menu.is-open {
  opacity: 1;
  transform: translateY(0);
}

Both states define opacity and transform, so the browser can interpolate between them. This makes the transition predictable.

Dropdown Transition Example

Dropdowns often need a combination of opacity, transform, visibility, and pointer events. This avoids transitioning display while still preventing hidden menus from being clicked.

.dropdown {
  opacity: 0;
  transform: translateY(6px);
  visibility: hidden;
  pointer-events: none;
  transition: opacity 160ms ease, transform 160ms ease, visibility 0s linear 160ms;
}

.menu-item:hover .dropdown {
  opacity: 1;
  transform: translateY(0);
  visibility: visible;
  pointer-events: auto;
  transition-delay: 0s;
}

The menu fades and moves smoothly, while visibility changes after the closing transition. This keeps the hidden dropdown from interfering with the page.

Transitioning Colors

Color transitions are common and usually cheap. They work well for links, buttons, borders, icons, and form feedback.

.link {
  color: #2563eb;
  transition: color 140ms ease;
}

.link:hover {
  color: #1e40af;
}

Keep color transitions short. Users should feel immediate feedback when they hover, focus, or press an interactive control.

Registered Custom Properties

Normal custom properties do not always animate smoothly because the browser may treat them as untyped values. With @property, a custom property can be registered with a syntax, initial value, and inheritance behavior.

@property --angle {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

.spinner {
  rotate: var(--angle);
  transition: --angle 300ms ease;
}

This is more advanced, but it shows that CSS transitions can work with typed custom properties when the browser understands how to interpolate the value.

Debugging Transitions

If a transition does not run, check whether the property is animatable, whether both states define usable values, whether another rule overrides the transition, and whether reduced motion rules are active.

  • Check that the property can animate.
  • Check starting and ending computed values.
  • Check transition-property spelling.
  • Check whether display is being toggled.
  • Check reduced motion media queries.
  • Check DevTools computed styles during state changes.

Staggered Transitions

Staggered transitions delay related elements by small amounts so they do not all move at the same instant. This can make menu items or cards feel more refined, but the delay should stay short.

.menu a {
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 160ms ease, transform 160ms ease;
}

.menu.is-open a:nth-child(2) {
  transition-delay: 40ms;
}

Staggering is visual polish. Do not delay important content so much that users feel the interface is waiting on animation.

Transition Events

JavaScript can listen for transition completion with the transitionend event. This is useful when a class should be removed only after a closing transition finishes.

CSS handles the motion, while JavaScript can coordinate state after the visual change. Keep that boundary clear so transitions remain easy to reason about.

Maintainable Transition Systems

For larger sites, define common durations and easing values as custom properties. Reusing the same timing values makes the interface feel consistent and prevents every component from having a slightly different motion personality.

A simple timing scale is easier to maintain than dozens of one-off transition values scattered across the stylesheet.

Consistency makes motion feel intentional.

Keep timing predictable.

Common Transition Mistakes

  • Using transition: all on every component.
  • Trying to transition display.
  • Making durations too slow for basic UI feedback.
  • Animating layout-heavy properties when transform would work.
  • Forgetting reduced motion preferences.
  • Adding transitions that make forms or menus feel delayed.

Transitions in CSS FAQ

What is a transition in CSS?

A transition smoothly animates a CSS property from one value to another when state changes.

Can display be transitioned?

Not in the normal smooth way. Use opacity, transform, visibility, or layout strategies instead.

What properties are best for transitions?

Transform and opacity are usually the most performance-friendly for UI motion.

Should I use transition all?

Usually no. Specify the properties you actually want to animate.


Continue learning CSS in order
Follow the topic sequence with the previous and next lesson.