Encodera

Thanks for reading!

Donate to the site — we work hard for you.

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".

Thanks for reading!

Donate to the site — we work hard for you.