Shots in this scene
Animating to height: auto
In one line
CSS cannot blend a length with the keyword auto, so a panel that opens to its natural height snaps. interpolate-size: allow-keywords, calc-size() and the 0fr to 1fr grid trick fix that, and ::details-content makes <details> accordions animate.
Think of it like this
An accordion door in a hall. You want it to open exactly as wide as the room behind it needs. You cannot say "slide open by 'as much as it takes'" to a motor that only understands centimetres. You must either measure the room or give the motor a rule that knows about rooms.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- interpolate-size
- An inherited property.
allow-keywordslets the browser blend intrinsic sizes such asautowith lengths. - calc-size()
- A function that lets you do maths on an intrinsic size, such as
calc-size(auto, size + 20px). - fr
- A grid unit meaning a share of the free space.
0fris zero and1fris all of it. - ::details-content
- A pseudo-element for the box around the content of a
<details>element. - intrinsic size
- A size the content decides (
auto,min-content,max-content), rather than a number you wrote.
Step by step
01Check support
Run "Three ways to open a panel". The console prints interpolate-size: true, calc-size(): true and ::details-content: true in this editor's browser.
02Open all three
Press Open / close all three. The first panel is at its full 29 px at 0 ms: no track, no motion. The second and third grow over 500 ms (about 19.8 px at 250 ms). Their tracks are height and grid-template-rows, both red.
03Add a line while open
Open them, wait, and press Add a line to each. All three jump from 29 to 58 px at once: nothing was animating, because no style changed.
04Use details
In "An accordion with ::details-content" press Battery, then Motor, and close Motor half way: it turns round smoothly from where it is, because it is an ordinary transition that reverses.
Try it yourself
- 1
Swap in calc-size()
In the first example remove
interpolate-size: allow-keywords;from.band change.b.open { height: auto; }to.b.open { height: calc-size(auto, size + 20px); }. After Run, press the button: the second panel still animates and ends at 49 px (29 + 20), 33.5 px at 250 ms. - 2
Switch on reduced motion
Turn on Reduced motion in the timeline and press the button: the second panel still slides, because the CSS has no reduce rule. Add
@media (prefers-reduced-motion: reduce) { .b, .c { transition: none; } }and run again: it snaps. - 3
Reverse in the middle
In the details example open Motor, then click it again about 200 ms later (the timeline cannot step this one, so click quickly in real time). The height turns round from where it is and walks back to its closed height. No jump.
Code & diagrams
The first panel has no help and snaps. The second uses interpolate-size and the third the grid trick. Pause at 250 ms and read the heights.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
interpolate-size: true
calc-size(): true
::details-content: trueThe same pattern the project's specs use, with a nested section. In a browser without ::details-content the sections simply snap open.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
sections: 3Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Take interpolate-size away
In the details example delete :root { interpolate-size: allow-keywords; }.
Myth vs fact
Myth
You need JavaScript to animate height: auto.
Fact
Three CSS-only ways exist today. Measuring scrollHeight and animating to a pixel value was the old way, and it breaks when the content changes.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
interpolate-sizeinherits, so putting it on:rootturns it on for the whole page. Some teams prefer to turn it on per component, because intrinsic-size animations are layout work. - ▸
block-sizeis the logical twin ofheight: it follows the writing mode, so the same rule works for vertical text. That is why the::details-contentpattern uses it. - ▸
Test with
@supports not selector(::details-content)to put a note or a different style in browsers that lack it. The example shows the pattern in its last rule.
Remember this
- 1
The problem this solves: a transition needs two numbers to blend.
height: 0is a number.height: autois a keyword that means "whatever the content needs", so the browser has nothing to blend and applies the end value at once. Accordions, menus and expanding cards all hit this. - 2
**
interpolate-size: allow-keywords.** Set it on:root(the property inherits) and the browser resolvesauto,min-content,max-contentandfit-contentto pixels at the moment it starts the transition, soheight: 0toheight: autoanimates. Chrome 129 shipped it. In the example, the second panel does this and the first panel, without it, snaps. - 3
**
calc-size().**height: calc-size(auto, size)is a function that wraps the keyword so it can be used in maths:calc-size(auto, size + 20px)is the content height plus 20 px. Acalc-size()value interpolates on its own withoutinterpolate-size. MDN (checked on 22 October 2026) listscalc-size()as limited availability and not Baseline: use it as an enhancement. - 4
The grid trick, for every browser. Put the content in a grid whose row goes from
0frto1fr, and give the inner wrapperoverflow: hidden; min-height: 0. Grid tracks interpolate, and1frof a single row is the content's height. No JavaScript and no measuring. It works wherever grid-template-rows transitions work, which is every current browser. - 5
Every one of them costs layout. The timeline shows
heightandgrid-template-rowsas red tracks: each frame the page is laid out. That is fine for one short accordion and bad for 100 of them at once. A FLIP of the content below (Topic 10.1) is the compositor-only alternative. - 6
**
<details>and::details-content.** The browser hides a closed<details>content withcontent-visibility: hidden, which cannot be animated by height. The::details-contentpseudo-element is a box around that content: give itblock-size: 0closed andautoopen, pluscontent-visibilitywithallow-discrete, and the accordion slides. MDN lists::details-contentas Baseline newly available since September 2025 (checked 22 October 2026). The Phase 10 build uses it. One limit of our tools: in Chromium 153 these transitions run but are not listed bygetAnimations(), so the timeline cannot show, pause or step them. Measure them with timers in the page. - 7
Fallbacks. A browser without
interpolate-sizestill ends up correct:height: autoapplies at once, so the panel opens without sliding. Write the rule so the snapped state is the working state, put the animation on top, and test with@supports (interpolate-size: allow-keywords)where the layout relies on it. - 8
Content that changes while open. If a line is added to an open panel, no style changed, so nothing transitions: the panel jumps to its new height. Reduced motion needs its own rule too: in the example, switching it on changes nothing until you add a reduce rule.
Explain it without notes
Why can't CSS animate height: 0 to height: auto by default, and what does interpolate-size change?
When would you use the grid trick instead of interpolate-size?
Practice
Write the CSS and HTML for a FAQ item that opens with the grid trick.
A card shows 3 lines of text and a "Read more" button that shows all of it. How do you animate it with interpolate-size?
Trade-offs
- ↔
Height animations cost layout on every frame. Prefer them for one or two small panels; for big lists, animate the siblings with FLIP.
Done when you can
You can explain why
autosnapsYou can use interpolate-size, calc-size() and the grid trick
You can animate a
<details>with ::details-contentYou can say what happens when the content changes while open