skip to content

Transitions

Interpolating a property between two states with a duration, easing curve, and delay. Expect questions about which properties are animatable, why transitioning to height: auto historically failed, and what transitionend fires for.

part ofCSSoverview, primer and where to startread it →
on this pageshow

questions

5

In CSS, what does the declaration `transition: opacity 300ms ease-in 100ms;` set, and how does the browser decide which of the two time values is the duration and which is the delay?

level: juniorimportance: must knowfreq 78%

answer

  1. four longhands hide behind one shorthand
  2. order-free by type, with one exception
  3. two time values, first one wins
  4. duration defaults to zero, property defaults to all

basics

~20 s

It animates only opacity over 300ms with an ease-in curve, starting after a 100ms delay. In the transition shorthand the components are order-free by type, except that the first time value is always the duration and the second is the delay.

solid answer

~40 s

The `transition` shorthand sets four longhands at once: `transition-property: opacity`, `transition-duration: 300ms`, `transition-timing-function: ease-in`, and `transition-delay: 100ms`. Within one shorthand item the value types can appear in any order — `transition: 300ms opacity` is legal — but if two `<time>` values are present, the first is the duration and the second is the delay. That ordering rule is the classic gotcha: `transition: opacity 100ms 300ms` is a 100ms animation that waits 300ms, not the other way round. The defaults matter too: without a duration nothing animates, because `transition-duration` is initially `0s`, while `transition-property` defaults to `all` and `transition-timing-function` to `ease`. For several properties you write a comma-separated list, each item with its own timing.

code

css · 18 lines
css
.panel {
  /* shorthand: property, duration, timing function, delay */
  transition: opacity 300ms ease-in 100ms;
}

.panel-longhand {
  transition-property: opacity;
  transition-duration: 300ms;
  transition-timing-function: ease-in;
  transition-delay: 100ms;
}

.panel-multi {
  /* one complete definition per comma-separated item */
  transition:
    background-color 150ms linear,
    border-color 300ms ease-out 50ms;
}

go deeper

for a junior

Be able to read a transition shorthand out loud and name each part, and state firmly that the first time value is the duration and the second is the delay.

for a middle

Explain the longhand defaults — all, 0s, ease, 0s — and what the shorthand resets when a component is omitted, plus how comma-separated lists give each property its own timing.

for a senior

Show judgment about scoping transitions: name the properties explicitly rather than using all, and be able to say why an unintended animation appeared after an unrelated rule was added.

for a principal

Own the convention across a codebase — a small set of shared duration and easing tokens, rules about which property categories may animate, and how those defaults are enforced in review.

## What a transition actually does A CSS transition interpolates a property between its old and new **computed value** whenever a style change gives that property a new value — a class toggle, a `:hover` or `:focus` state, an inline style write, a media-query flip. You never say "animate now"; you declare *in advance*, on the element, which properties are allowed to move and over how long. When the new value arrives, the browser compares the before-change value with the after-change value and, if the property is listed in `transition-property` and the two values can be interpolated, it plays the change out over time instead of applying it instantly. ## The four longhands behind the shorthand - `transition-property` — which properties may transition. Initial value: `all`. `none` disables transitions entirely. - `transition-duration` — how long the interpolation takes. Initial value: `0s`, which is why a `transition` declaration with no time value does nothing visible. - `transition-timing-function` — the easing curve mapping elapsed time to progress. Initial value: `ease`. - `transition-delay` — how long to wait before starting. Initial value: `0s`. There is a fifth longhand, `transition-behavior`, used to opt discrete properties in; it is not part of the everyday four. ## Parsing the shorthand ```css .card { transition: opacity 300ms ease-in 100ms; } ``` This expands to property `opacity`, duration `300ms`, timing function `ease-in`, delay `100ms`. The grammar is order-independent **by type** — the parser recognises a property name, a timing function, and time values by what they are, not by position — with one exception: when a shorthand item contains two `<time>` values, the **first is the duration and the second is the delay**. So all three of these are identical: ```css transition: opacity 300ms ease-in 100ms; transition: 300ms opacity 100ms ease-in; transition: ease-in 300ms 100ms opacity; ``` and `transition: opacity 100ms 300ms` is a *different* rule — 100ms of animation after a 300ms wait. Swapping those two numbers by accident is one of the most common CSS transition bugs, and it looks like "the animation is too slow to start" rather than a syntax error. Because it is a shorthand, any longhand you omit is reset to its initial value. Writing `transition: opacity 300ms` after a rule that set `transition-delay: 200ms` silently puts the delay back to `0s`. ## Several properties at once Each comma-separated item is a complete transition definition: ```css .button { transition: background-color 150ms linear, transform 300ms cubic-bezier(0.2, 0, 0, 1) 50ms; } ``` If you use the longhands instead and the lists have different lengths, the shorter lists are repeated to match `transition-property`, so `transition-property: color, background-color; transition-duration: 200ms;` gives both properties 200ms. ## Why `transition: all` gets pushback `all` is the initial value and is convenient, but it commits the element to animating every animatable property that ever changes — including ones a future rule adds, and including changes inherited from an ancestor's state change. The result is components that animate things nobody intended, such as a layout property shifting slowly during a state change. Listing the properties explicitly documents intent and makes the rule reviewable. ## Negative delay `transition-delay` accepts negative values. `transition-delay: -100ms` on a 300ms transition starts it already one third of the way through: it begins immediately, at the value it *would* have had 100ms in, and finishes 200ms later. ## When no transition happens at all Even with a correct shorthand, nothing animates if the combined duration is zero, if the property is not listed in `transition-property`, if the computed value did not actually change (setting `color: red` on an element that already computes to `red` is not a change), if the two values cannot be interpolated (a length to `auto`, for example), or if the element is being rendered for the very first time and therefore has no before-change value to start from.

  • If you write `transition-duration: 400ms` and nothing else, which properties will animate?
    All animatable ones, because `transition-property` keeps its initial value of `all`. Any property whose computed value changes on that element — colour, spacing, borders, opacity — will take 400ms. That is usually more than intended, which is why most teams list properties explicitly instead of relying on the default.
  • What does a negative `transition-delay` do?
    It starts the transition immediately but fast-forwarded. With `transition: transform 300ms; transition-delay: -100ms`, the element jumps straight to the value it would have had 100ms in and finishes 200ms later. If the negative delay is larger than the duration, the transition is already complete and nothing visible happens.
  • Why does adding a `transition` to a rule that already set `transition-delay` sometimes lose the delay?
    Because `transition` is a shorthand: it sets every one of its longhands, and any component you leave out is reset to its initial value. A later `transition: opacity 300ms` overwrites the earlier `transition-delay` with `0s`. Either include the delay in the shorthand or set the longhands consistently.

saying these in an interview costs you the question

  • Thinks the first time value in the shorthand is the delay
  • Says a transition runs once on page load without any state change
  • Believes `transition` alone animates something without a duration
  • Claims shorthand components must appear in a fixed order
  • Assumes `transition: all` is free and always the right default

context

open as a page

Why does a CSS transition on `height` do nothing when the target value is `height: auto`, and which techniques actually produce a smooth open and close?

level: middleimportance: must knowfreq 64%

basics

~20 s

A transition interpolates between two computed values of the same type, and auto is a keyword, not a length, so there is nothing to interpolate and the height jumps. Common fixes are animating max-height, animating a wrapper's grid-template-rows from 0fr to 1fr, or opting keywords in with interpolate-size.

open as a page

In CSS, what do the four numbers in `cubic-bezier(0.68, -0.55, 0.27, 1.55)` represent, and why can a transition using it overshoot its final value?

level: middleimportance: should knowfreq 44%

basics

~20 s

They are the x and y coordinates of the two control points of a cubic Bezier curve whose endpoints are fixed at (0,0) and (1,1), mapping elapsed time to progress. The x values must stay within 0 to 1, but y may go below 0 or above 1, which produces anticipation and overshoot.

open as a page

In CSS transitions, what does the `transitionend` event fire for on an element that transitions several properties at once, and name two situations where it never fires at all.

level: middleimportance: should knowfreq 42%

basics

~20 s

transitionend fires once per transitioned longhand property, not once per element, and it bubbles — so a three-property transition dispatches three events, distinguished by the event's propertyName. It never fires when no transition was generated (zero duration, or no computed-value change) or when the transition is cancelled, which dispatches transitioncancel instead.

open as a page

A CSS class toggle takes an element from `display: none; opacity: 0` to `display: block; opacity: 1` with `transition: opacity 300ms`. Why is there no fade-in, why does the fade-out also fail, and which CSS fixes each?

level: seniorimportance: should knowfreq 32%

basics

~20 s

There is no fade-in because an element leaving display: none is rendered for the first time and has no before-change value to transition from — @starting-style supplies one. The fade-out fails because display is a discrete property that flips immediately, hiding the element; transition-behavior: allow-discrete defers that flip to the end.

open as a page