How to animate height to auto
Short answer
Allow animating to keywords like auto with one line on :root:
<style>
:root { interpolate-size: allow-keywords; }
.panel { height: 0; overflow: hidden; transition: height 0.4s; background: lavender; }
label:has(:checked) + .panel { height: auto; }
</style>
<label><input type="checkbox"> Show details</label>
<div class="panel">
<p>Delivery takes 2–3 days. Returns are free within 30 days.</p>
</div>Previously transition could not animate height: auto: the block opened with a jump. interpolate-size: allow-keywords allows a smooth transition between a number and auto, min-content or fit-content. The property is inherited, so setting it once on :root is enough.
For one property only
If you do not want to turn this on for the whole page, use the calc-size() function right in the value: height: calc-size(auto, size);. The result is the same, but only for this rule.
A fallback for every browser
interpolate-size is not in every browser — there the block opens without animation. The grid trick works everywhere: it animates the row height from 0fr to 1fr, and 1fr is exactly the content height:
<style>
.wrap { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 0.4s; background: honeydew; }
.wrap > div { overflow: hidden; }
label:has(:checked) + .wrap { grid-template-rows: 1fr; }
</style>
<label><input type="checkbox"> Show details</label>
<div class="wrap">
<div><p>Delivery takes 2–3 days. Returns are free within 30 days.</p></div>
</div>The inner <div> with overflow: hidden is required: without it the content does not shrink to zero.
Common mistake
Animating max-height with a margin, for example to 1000px. The transition runs to a thousand pixels while the content takes a hundred, so opening finishes almost instantly and closing starts with a pause. Use interpolate-size or the grid trick — they animate the real height.
Functions used in this recipe
- interpolate-size — Allows transitions to and from size keywords such as `auto`, `min-content` and `fit-content` — finally, `height: 0` → `height: auto`.
- height — The height of an element.
- transition — Animates the change of a property from the old value to the new one.
- grid-template-rows — The number and heights of the rows of a grid.
- :has() — Matches an element that contains something matching the selector in brackets — the "parent selector".