CSS Transitions Finally Reach auto Dimensions
One of the longest-running pain points in CSS is slowly coming to an end: the ability to transition an element to its own natural size. Chrome has recently shipped initial support, making it possible to animate from a fixed value to an intrinsic auto dimension without JavaScript measuring the element first.
The Problem: You Can't Animate to an Unknown Value
The core issue is that CSS doesn't inherently know an element's computed height or width. To transition from, say, height: 0 to a fully expanded state, you need concrete numbers on both ends of the animation. Designers and developers have historically worked around this by hard-coding a fixed pixel value:
.panel {
height: 0;
transition: height 0.25s ease-in;
&.expanded {
height: 300px;
}
}
That approach breaks down when content changes over time—new text, different fonts, or added padding all require updating that fixed value manually. This is why JavaScript has been the default solution for expand/collapse interactions, using measurement APIs to determine and set the target height.
The same challenge applies across all dimension properties: height, block-size, width, and inline-size. Any property that can be set to auto has the same fundamental limitation.
calc-size() Bridges the Gap
Chrome's current implementation leverages the CSS calc-size() function. It can accept auto as an argument and compute the actual numeric value, allowing transitions to work naturally between a defined size and the element's intrinsic size:
.panel {
height: 0;
transition: height 0.25s ease-in;
&.expanded {
height: calc-size(auto);
}
}
While calc-size() supports more complex expressions, simply passing auto into it is what unlocks the smooth two-way transition between a fixed state and the element's natural dimensions.
A practical demonstration: consider a calendar component with a floating notification button. Clicking it expands a panel upward to reveal pending invites; clicking again collapses it. JavaScript handles just the click event and toggles a class, while the height animation happens entirely through CSS:
.invite-panel {
height: 0;
overflow-y: clip;
transition: height 0.25s ease-in;
}
The JavaScript side only needs to override the CSS height with an inline style of auto, letting calc-size() resolve the value:
<div class="invite-panel" style="height: calc(auto)">
With the transition property set on height, the browser animates smoothly between the two states. As with any motion on the web, respecting prefers-reduced-motion remains an important practice to reduce or eliminate animation for users who need it.
Transitioning from display: none
A natural follow-up question: can this technique animate elements that start as display: none? Not quite directly. A hidden element isn't technically at zero height—it still computes to auto for its dimensions unless explicitly declared otherwise. So even with calc-size(), the transition has no numeric starting point:

The workaround relies on two newish CSS features working together. The element begins with both display: none and height: 0, and an .open class switches it to display: block with a height of calc-size(auto). The missing link is the transition-behavior: allow-discrete declaration, which tells the browser to transition discrete properties as well as interpolable ones.
.element {
/* hard mode!! */
display: none;
transition: height 0.2s ease-in-out;
transition-behavior: allow-discrete;
height: 0;
@starting-style {
height: 0;
}
&.open {
height: calc(auto);
}
}
Discrete properties—and display is one of them—normally jump instantly between states. By setting allow-discrete, the browser will interpolate them. But since auto computes to a discrete value for display purposes, you need one more piece: @starting-style. This at-rule defines the initial style from which the transition should begin, effectively overriding the discrete jump to start at height: 0:
.element {
/* etc. */
@starting-style {
height: 0;
}
}
With this combination, an element can transition from fully hidden to its intrinsic height in one smooth motion—no hard-coded pixel values and no JavaScript measurement required.



