Transitions to height:auto and width:auto without any JavaScript
For years, a transition to or from an auto value in CSS simply did not work, because the browser had no way to compute a numeric intermediate value for auto. calc-size() and the interpolate-size property change that fundamentally: the browser can now resolve a numeric start or end value for auto itself and animate cleanly between them, with no JavaScript measurements and no fixed pixel values.
Table of Contents
- 1. Why transitions to auto values never used to work
- 2. calc-size(): translating auto into an animatable value
- 3. interpolate-size: allow-keywords as a document-wide switch
- 4. Typical use cases: accordions, dropdowns, dynamic content
- 5. Browser support reality: where calc-size() stands today
- 6. Fallback strategy for browsers without support
- 7. Rendering performance compared to the JavaScript solution
- 8. Combining with container queries and custom properties
- 9. Recommendation: when switching over already pays off today
- 10. Summary
- 11. FAQ
1. Why transitions to auto values never used to work
CSS transitions interpolate between two numeric values by having the browser compute an intermediate value on every frame, for example between 100px and 300px. The keyword auto, on the other hand, is not a numeric value but an instruction to the layout system to derive the size from the content. The browser historically had no notion of an intermediate step between a fixed number and such an instruction, which is why a transition to height: auto either never started or jumped straight to the end value with no visible animation.
Developers have worked around this for years using JavaScript: before the animation, the content's actual height was measured with scrollHeight, that number was set as a fixed pixel value, and only then did the transition start. That works, but adds JavaScript dependency, layout thrashing from the measurement itself, and fragility with dynamic content whose height can still change after measuring, for example due to images loading in later.
2. calc-size(): translating auto into an animatable value
The function calc-size(<basis>, <calculation>) takes a keyword like auto, fit-content or min-content as its first argument and passes its resolved, actual size value into a calculation in the second argument. That calculation can be as simple as size, meaning the raw value itself, or an actual computation like size * 1.2, for example to add a bit of buffer above the content size.
The crucial point is that calc-size() returns a genuine, numeric value to the transition engine, even when the basis was auto. That lets the browser interpolate between a fixed starting value like 0px and the result of calc-size(auto, size) exactly like it would between two fixed pixel values, because internally there is already a number, not the abstract keyword anymore.
.accordion-panel {
height: 0px;
overflow: hidden;
transition: height 0.3s ease;
}
.accordion-panel.open {
/* calc-size resolves 'auto' into a real number
that can be animated */
height: calc-size(auto, size);
}
3. interpolate-size: allow-keywords as a document-wide switch
While calc-size() makes individual values animatable in a targeted way, the interpolate-size property offers a global switch that acts directly on elements with an auto value, without calc-size() having to be written explicitly. Set on :root { interpolate-size: allow-keywords; }, this property allows the browser to treat keywords like auto, fit-content and min-content as interpolable values everywhere in the document.
That is the more convenient, if less precise, route: instead of adding calc-size() to every single transition declaration, a single global rule makes all transitions and animations in the project work to and from auto. The trade-off is less control over individual cases, in exchange for noticeably less writing effort on large projects with many accordion or dropdown components.
:root {
interpolate-size: allow-keywords;
}
.dropdown-menu {
height: 0;
overflow: hidden;
transition: height 0.25s ease-out;
}
.dropdown-menu[data-open] {
/* No calc-size needed -- interpolate-size allows
the direct transition to auto */
height: auto;
}
4. Typical use cases: accordions, dropdowns, dynamic content
The classic use case is an accordion panel whose content can vary widely in length, because it comes from a CMS or contains user input. With a fixed target height, every content change would also require adjusting the CSS height, which in practice almost never gets maintained. With calc-size(auto, size), the animated target height adapts automatically, no matter how long the content currently is.
A second common case is a dropdown menu whose width depends on the length of its longest entry. Here, fit-content is a better basis than auto, because it snaps the width exactly to the content instead of claiming the full available width of the parent. fit-content can also be used as a basis inside calc-size() to make it animatable.
5. Browser support reality: where calc-size() stands today
Chrome and Edge support both calc-size() and interpolate-size since Chrome 129, released in late 2024. Firefox and Safari had not shipped complete, production-ready support at the time of writing, which is why a project needing cross-platform animated auto transitions should not rely on this feature exclusively right now.
In practice this means calc-size() can already be used today as a progressive enhancement, without users on unsupported browsers seeing anything broken, as long as a sensible fallback exists. For critical UI elements that must behave identically across every browser, JavaScript-based scrollHeight measurement remains the safer choice for now, but can be gradually replaced with calc-size() as support grows sufficient.
6. Fallback strategy for browsers without support
The cleanest fallback uses @supports to apply calc-size() only where the browser actually understands it. Outside the @supports rule, the old, robust solution stays in place: either a rough, fixed maximum height with a max-height transition, which is not perfect but works without JavaScript, or the classic JavaScript measurement for cases where the animation needs to be exact.
It matters not to treat the fallback as an afterthought, because without it, users on unsupported browsers either see an abrupt jump with no animation or, worse, a panel that never opens because the height incorrectly stays at zero. A tested, functional fallback is therefore mandatory, not optional.
.panel {
overflow: hidden;
max-height: 0;
transition: max-height 0.3s ease;
}
.panel.open {
/* Fallback: rough but functional maximum height */
max-height: 1000px;
}
@supports (height: calc-size(auto, size)) {
.panel {
max-height: none;
height: 0px;
transition: height 0.3s ease;
}
.panel.open {
height: calc-size(auto, size);
}
}
7. Rendering performance compared to the JavaScript solution
The JavaScript measurement with scrollHeight forces a synchronous reflow, because the browser has to compute the layout right now before it can return the value. With frequently triggered animations, for example a long list of accordion items that all open at once, that forced reflow adds up noticeably and can cause visible jank.
calc-size() and interpolate-size, by contrast, run entirely inside the browser's rendering pipeline, without the detour through JavaScript execution and without an externally forced synchronous reflow. That makes the solution not only more elegant in code, but measurably smoother on devices with limited processing power, especially with many elements animating at once.
8. Combining with container queries and custom properties
Because calc-size() is a full-fledged CSS value, it can be combined with custom properties, for example to make an extra buffer above the content size configurable instead of hard-coding it. That is especially useful in design systems where multiple components share the same animation behavior but need slightly different buffer sizes.
Combined with container queries, the behavior of the auto transition can also be controlled responsively, for example disabling the animated height change entirely on small containers, where there is no room for an expanding animation anyway, while larger containers get the full calc-size() transition.
.panel {
--extra-buffer: 8px;
height: 0px;
transition: height 0.3s ease;
}
.panel.open {
height: calc-size(auto, size + var(--extra-buffer));
}
9. Recommendation: when switching over already pays off today
For internal tools, admin interfaces, or projects with a known, controlled browser audience that runs predominantly on Chromium, switching to calc-size() and interpolate-size already pays off, because the code becomes noticeably simpler and no JavaScript measurement logic needs to be maintained anymore.
For public websites with a broad browser spread, the hybrid approach with @supports safeguarding, as shown in the fallback section, is recommended: users on modern Chromium browsers get the precise, performant native solution, while everyone else gets a functionally equivalent, if technically simpler, fallback.
| Approach | JavaScript needed | Reflow cost | Browser support |
|---|---|---|---|
| scrollHeight measurement | Yes | Synchronous reflow on every measurement | All browsers |
| max-height trick | No | Low, but imprecise timing | All browsers |
calc-size(auto, size) |
No | None, runs inside the rendering pipeline | Chrome/Edge 129+ |
interpolate-size: allow-keywords |
No | None | Chrome/Edge 129+ |
Mironsoft
Modern CSS, layout architecture and rendering performance
CSS that stays maintainable instead of breaking with every change?
We review existing stylesheets for specificity chaos and layout thrashing, then build a CSS architecture with cascade layers, custom properties and modern layout primitives that still makes sense after the tenth feature.
CSS Audit
Systematically uncovering specificity issues, cascade conflicts and unused selectors.
Architecture Refactoring
Introducing cascade layers, custom properties and design tokens cleanly.
Performance Tuning
Fixing layout thrashing, expensive selectors and rendering bottlenecks.
10. Summary
calc-size() and interpolate-size: The Essentials at a Glance
calc-size()
Resolves keywords like auto into a real, animatable numeric value, applied per declaration.
interpolate-size
Global switch that allows auto transitions across the whole document without writing calc-size() explicitly.
Support status
Fully supported in Chrome and Edge from version 129 onward, not yet in Firefox and Safari at the time of writing.
Fallback
@supports safeguarding with a max-height trick or JavaScript measurement secures behavior in unsupported browsers.