Transitions

Learn to animate smoothly between CSS property values using transition-property, transition-duration, transition-timing-function, and transition-delay, plus the transition shorthand.

Without a transition, a CSS property change — a hover color, a class toggle — happens instantly, in a single frame. A transition tells the browser to smoothly interpolate between the old value and the new value over a period of time, producing the small, polished animations you see constantly in real interfaces: a button that gently darkens on hover, a menu that fades in, a card that eases into its lifted position.

The four transition properties

A CSS transition is controlled by four longhand properties that are almost always combined into the transition shorthand in real code, but understanding each individually makes the shorthand's order easy to remember.

• transition-property — which CSS property (or properties) should animate, e.g. background-color, or all for every animatable property • transition-duration — how long the animation takes, e.g. 0.3s or 300ms • transition-timing-function — the pacing curve of the animation over its duration (ease, linear, ease-in-out, or a custom cubic-bezier) • transition-delay — how long to wait before the animation starts, e.g. 0.1s

transition-timing-function: the pacing curve

The timing function controls HOW the value changes over the duration, not how long it takes. linear moves at a constant, mechanical speed throughout. ease (the default) starts slowly, speeds up in the middle, and slows down again at the end — this reads as the most 'natural' motion for most UI purposes and is a sensible default. ease-in starts slow and ends abruptly fast; ease-out starts fast and eases to a gentle stop (often ideal for elements entering the screen, since it feels like they're settling into place). ease-in-out combines both — slow start, slow end.

For full custom control, cubic-bezier(x1, y1, x2, y2) defines an exact custom easing curve using two control points. You rarely need to hand-write these values — many designers pick a curve visually using an online cubic-bezier picker tool and paste the resulting function into their CSS.

The transition shorthand

In practice, most developers write the shorthand: transition: property duration timing-function delay;. For example transition: transform 0.3s ease-out 0.05s; — animate the transform property, over 0.3 seconds, easing out, starting after a 0.05 second delay. If delay is omitted it defaults to 0s (no delay). Multiple properties can be transitioned with different settings by comma-separating full transition specs: transition: transform 0.3s ease, opacity 0.2s linear;.

Common UI patterns

A hover fade is one of the simplest and most common patterns: set an element's opacity or background-color to transition, then change that same property inside a :hover rule — the browser animates automatically between the two states, and automatically reverses the animation when the mouse leaves, with no extra code needed.

A 'button press' pattern often transitions transform: scale() — a slight scale-down on :active (while the mouse button is held down) gives tactile, physical-feeling feedback that the click registered, and reverts smoothly when released.

Transitions only animate a change that actually happens — they don't run on page load unless the property genuinely changes from an initial state to a new one (for instance, toggling a class with JavaScript, or a pseudo-class like :hover or :focus becoming active).

Transition longhand and shorthand

transition-property: all; transitions every animatable property that changes — convenient but can be less performant and less predictable than naming specific properties. The shorthand's order is property, duration, timing-function, delay — duration must come before delay if both are present.

.el {
  /* Longhand form */
  transition-property: background-color;
  transition-duration: 0.3s;
  transition-timing-function: ease-in-out;
  transition-delay: 0s;

  /* Shorthand: property duration timing-function delay */
  transition: background-color 0.3s ease-in-out;

  /* Multiple properties, different timings */
  transition: transform 0.25s ease-out, opacity 0.4s linear;

  /* Custom easing curve */
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

Code Examples

The link's text color smoothly fades from the lighter blue to the darker blue over 0.2 seconds when hovered, and fades back automatically when the mouse leaves — no separate 'un-hover' transition is needed.

transition: color 0.2s ease;

While the mouse button is held down on .button, it shrinks very slightly, giving physical, tactile-feeling feedback that the click registered. Releasing the mouse smoothly returns it to full size.

.link {
  color: #2b6cb0;
  transition: color 0.2s ease;
}
.link:hover {
  color: #1a4971;
}
.button {
  transform: scale(1);
  transition: transform 0.15s ease-out;
}
.button:active {
  transform: scale(0.96);
}

Timing function shapes

Each curve describes speed over time, not total duration — duration is set separately.

linear      ease         ease-in      ease-out
  /         ⟋‾⟍           ⟋            ‾⟍
 /         ╱   ╲          ╱              ╲
/         ╱     ╲        ╱                ╲
constant   slow-fast-slow  slow then fast  fast then slow

Important Rules

The shorthand order is: transition-property, transition-duration, transition-timing-function, transition-delay.

transition-duration must be specified for any animation to be visible — without it (default 0s) the change is instant.

A transition only plays when the watched property's value actually changes, e.g. via :hover, :focus, :active, or a JS class toggle.

ease is the default timing function and a safe general-purpose choice; ease-out often suits elements entering/appearing.

Comma-separate multiple full transition specs to animate different properties with different durations or curves.

Where transitions matter for real products

Interfaces without any transitions feel abrupt and low-quality even if every individual state is styled correctly — the human eye is very sensitive to sudden, jarring changes. Product teams often specify duration and easing values (like '200ms ease-out for hover states, 300ms for modal enter/exit') as part of a formal design system, precisely because consistent transition timing across a whole product contributes significantly to a polished, professional feel..card:hover { transition: transform 0.3s ease; transform: scale(1.05); }.card { transition: transform 0.3s ease; }.card:hover { transform: scale(1.05); }

The transition property should be declared on the element's base/resting state, not only inside :hover — otherwise there is nothing telling the browser to animate the transition back to normal when the mouse leaves, causing the reverse to snap instantly.

transition: 0.3s color;

transition: color 0.3s;

In the shorthand, the property name comes before the duration. While some browsers are forgiving about order in simple cases, writing property first is the correct, unambiguous order especially once a timing-function or delay is added.

[True / False] True or False: If you only define transition: background-color 0.3s ease; on .box:hover, and not on the base .box rule, hovering will still smoothly fade the background in, and moving the mouse away will also smoothly fade it back out. A. True — the transition applies to both entering and leaving the hover state equally B. False — hovering in will fade smoothly (the :hover rule's transition applies while the property is changing to the hover value), but leaving will snap back instantly, since no transition is defined on the base .box state to animate the return

B. False — hovering in will fade smoothly (the :hover rule's transition applies while the property is changing to the hover value), but leaving will snap back instantly, since no transition is defined on the base .box state to animate the return — A transition property only takes effect for state changes while that transition declaration itself applies. When the mouse leaves and :hover no longer matches, the element returns to the base rule, which lacks a transition declaration — so that specific change happens instantly. This is why best practice puts transition on the base selector.

Style a .nav-link so its background-color transitions to a highlight color over 0.2 seconds with ease-out timing, and its underline (via border-bottom-color, assume it's already set up) transitions over 0.35 seconds with a 0.1 second delay, all in one shorthand declaration..nav-link { transition: background-color 0.2s ease-out, border-bottom-color 0.35s ease 0.1s; }

You need two comma-separated transition specs in a single transition property, each with its own property, duration, timing-function, and (for the second) delay.