Command Palette

Search for a command to run...

Hectal
Shots in this scene
SHOT 3.2Beginner ~23 min · shot 2 of 6

What Can and Can't Transition

In one line

The browser can blend numbers, lengths, colours and shapes, so those properties transition smoothly. Keyword properties such as display have no in-between, so they flip, and values like height: auto cannot be blended with a length at all.

0/6 · 0%

Think of it like this

A painter can mix any amount of orange into blue and get every shade between. But a door is open or closed. Half-way between open and closed is not a place a door can be. CSS properties are like that. Numbers and colours are paint: the browser can mix them. Keywords such as display: none or position: absolute are doors: there is no in-between to show.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

interpolate
To work out the values between a start value and an end value, for example 40% of the way from 0 to 200 is 80.
discrete property
A property whose values are keywords with nothing between them, like display or text-align. It switches instead of blending.
allow-discrete
The transition-behavior value that lets a discrete property take part in a transition, flipping at 50%. display and visibility have special rules.
@starting-style
A rule that gives an element the style to start from on its first frame, when it has no earlier style to change from.

Step by step

01Read the table

Run "Which properties transition?". Each line says whether the browser created a transition for one property with transition: all 1000ms linear. Nine make one. background-image, height (to auto), display, text-align, border-style and position do not.

02Slow down the flip

Run "Four ways to flip", press 0.1×, then Switch all four, and scrub to the middle. The text-align box moves its label to the right between 480 and 520 ms. The visibility box stays until the very end.

03Watch display leave

In the same example, the third box fades and then disappears at 1000 ms (display with allow-discrete). The fourth box, which has no allow-discrete, is gone in the first frame, so its fade is never seen.

04Grow to auto

In "Auto, gradients and colours" press Switch. The first box jumps to its full height. The second grows over 1000 ms, because of interpolate-size: allow-keywords.

Try it yourself

  1. 1

    Find the half-way flip

    In "Four ways to flip", press 0.1×, press Switch all four, then pause and step one frame at a time near 500 ms. The text-align track is CSSTransition · text-align with a layout mark. The label is on the left at 480 ms and on the right at 520 ms, with no movement between: a flip, not a slide.

  2. 2

    Take allow-discrete away

    In the #d rule, delete display 1000ms linear allow-discrete, so only the opacity item is left. Press Switch all four: the third box now vanishes in the first frame, exactly like the fourth, and its opacity fade is never seen because it is already gone.

Code & diagrams

Which properties transition?

The script changes each property with `transition: all 1000ms linear` set, and asks the browser how many transitions it created. A tick means it made one.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

opacity: transition
width: transition
background-color: transition
font-weight: transition
z-index: transition
box-shadow: transition
transform: transition
filter: transition
visibility: transition
background-image: no transition
height: no transition
display: no transition
text-align: no transition
border-style: no transition
position: no transition
Four ways to flip

All four take 1000 ms. Slow the page to 0.1x, press Switch all four and step: text-align changes at the half-way point, visibility at the end, display fades then leaves, and the fourth box leaves at once.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.
Auto, gradients and colours interpolate-size needs Chromium 129 or newer+

Press Switch. Box 1 (height: auto) jumps, box 2 (interpolate-size) grows, gradient 1 swaps, gradient 2 animates a registered --stop, and the two colour bars blend in sRGB and in Oklab.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

interpolate-size supported: true

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Transition a height to auto

Give a box height: 0; overflow: hidden; transition: height 300ms; and a class that sets height: auto. Add the class.

terminal
$ Add the class and look at the timeline
── what you'll see ──
The box jumps to its full height in the first frame. No `transition · height` track appears. (In the table above, `height: no transition`.)

Break #2

Fade out with display: none but no allow-discrete

In "Four ways to flip", look at the fourth box: #n { transition: opacity 1000ms linear; } and #n.on { display: none; opacity: 0; }.

terminal
$ Press Switch all four
── what you'll see ──
The fourth box disappears at once, and no track at all appears for it: not even for `opacity`, because an element with `display: none` has no box to animate.
Measured: with `display: none` and no `allow-discrete`, there is no `display` transition and the display change applies in the first frame.

Myth vs fact

Myth

transition: all animates every property.

Fact

It animates every property the browser can interpolate. Discrete ones (text-align, display, border-style) are left out unless you add allow-discrete, and height: auto and gradients cannot be blended at all.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    While an element fades out with visibility or display held on, it is still visible: it receives clicks and keyboard focus until the end. For a closing menu, set pointer-events: none and the inert attribute at the start of the exit, so it cannot be used while it fades.

  • ▸

    transition-behavior is not inherited and is reset by the shorthand: transition: opacity 1s (no keyword) sets transition-behavior: normal, which switches allow-discrete off. Put the keyword inside each item of the list, or set transition-behavior after the shorthand.

Remember this

  1. 1

    The problem this solves: transition: all 300ms looks as if it animates everything. It does not. A menu that should fade out vanishes at once, a panel that should grow to its natural height jumps, a gradient swaps in a single frame, and nothing says why. Knowing the rule in advance saves hours of guessing.

  2. 2

    Interpolation. To make the frames between two values the browser must interpolate: work out a value at, say, 37% of the way. It can do that for numbers (opacity, z-index, which rounds to a whole number), lengths and percentages (width, padding), colours, transforms, shadows, filters and shapes such as clip-path with matching functions. In the table below, nine of the fifteen properties made a transition. Each property's specification says how it can be animated (MDN lists them under animatable properties).

  3. 3

    Discrete properties flip at 50%. A property whose values are keywords (text-align, border-style, position, display) has no in-between. By default the browser starts no transition for it, and transition: all leaves it out (measured: text-align: no transition). Add transition-behavior: allow-discrete (or the keyword in the shorthand, transition: text-align 1s allow-discrete) and it holds the old value for the first half and shows the new one for the second. Frozen at 480 ms of 1000 ms, text-align was start. At 520 ms it was right.

  4. 4

    **visibility and display break the 50% rule on purpose.** For visibility, the element counts as visible for the whole transition if either end is visible. Fading out to hidden keeps it visible to the very end (measured: visible at 999 ms, hidden at 1010 ms). With allow-discrete, display works the same way: going to none it stays block until the end (measured: block at 999 ms, none at 1010 ms), and coming from none it turns on at the start. That lets an element fade out and then leave the layout.

  5. 5

    **Entering from display: none needs @starting-style.** A transition needs a before-change style, and an element that was display: none has none. @starting-style { .panel.open { opacity: 0; } } supplies the values to start from on the first frame. The exercise fade-out-and-hide needs both allow-discrete and @starting-style: removing either breaks the fade (measured). Support (MDN, read 24 Oct 2026): @starting-style is Baseline 2024 (in all major browsers since August 2024); transition-behavior and interpolate-size are marked limited availability. Chrome shipped the first two in 117 and interpolate-size in 129. For older browsers, write the transition twice, once without allow-discrete and once with it: a browser that does not know the keyword drops the second line and keeps the first. The panel then still opens and closes, just without the fade.

  6. 6

    **height: auto cannot transition.** The browser cannot blend 0px with the keyword auto (measured: no transition, the box jumps). The old workaround animates max-height to a big number, which gets the timing wrong, because the duration covers the whole range. Better: interpolate-size: allow-keywords makes auto animate (Chromium; elsewhere the box jumps, which is an acceptable fallback), or animate grid-template-rows from 0fr to 1fr on a grid wrapper (measured: it animates, but not in a straight line: 29.5 px of 118 at the half-way time). Phase 10 shows the general fix, FLIP.

  7. 7

    Gradients and colour spaces. A gradient is an image, and images flip: a gradient to another gradient changes in one frame (measured). To animate one, animate the numbers inside it: register @property --stop { syntax: "<percentage>"; ... } and transition --stop (measured: 50% at 500 ms), or fade the opacity of a second layer, which is cheaper. Plain colours do interpolate. Between rgb(255 0 0) and rgb(0 0 255) the middle is rgb(128, 0, 128) (sRGB). With a modern function such as oklch(), Chromium blends in Oklab instead, and the in-between colours look cleaner.

Explain it without notes

01

Why does transition: all 300ms not fade out an element that you then set to display: none?

02

What is the difference between how visibility and text-align behave under allow-discrete?

03

How would you animate a panel to its natural height, and what do you do for a browser that does not support your method?

Practice

01

A tooltip is display: none and gets .show. Write the CSS so it fades in over 150 ms and fades out over 150 ms and leaves the layout at the end.

02

A button's gradient background goes from orange-to-blue to blue-to-orange on hover. It snaps. Make it animate with @property.

Done when you can

  • You can tell from the property's type whether it will transition, flip or do nothing

  • You can fade an element out and take it out of the layout with allow-discrete

  • You know why entering from display: none needs @starting-style

  • You have a plan for height: auto and for gradients, and a fallback when it is not supported