How to animate height: auto in CSS (accordion)

A transition from height 0 to height auto does nothing in CSS. Three ways to animate an accordion open and closed anyway, the grid-template-rows trick that works in every current browser, the new details-content way, and the max-height hack to avoid. Tested in Chrome and Firefox.

Short answer: Wrap the content in a grid and animate the row instead of the height: grid-template-rows: 0fr when closed, 1fr when open, with transition: grid-template-rows 0.35s, and min-height: 0; overflow: hidden on the one child. It works in every current browser. The newer interpolate-size: allow-keywords lets height: auto animate directly, but not every browser has it yet.

You write transition: height 0.3s, toggle from height: 0 to height: auto, and the panel jumps open without any animation. It is not a bug in your code: browsers cannot work out the values between 0 and "whatever the content needs", so for years auto simply could not be animated.

Why does height: auto not animate?

A transition needs two numbers to move between. auto is not a number until the browser has laid out the content, so the classic rule was: no transition to or from auto. Widths and heights in fr units of a grid are different: 0fr and 1fr are numbers, the grid works out the real height for each step, and the panel opens smoothly to exactly its content's height.

The grid trick: an accordion that works everywhere

This is Motion Studio's accordion. Only the button opens and closes it, the button tells screen readers whether it is open (aria-expanded), and the closed panel's links are kept out of the Tab order (visibility):

<div class="accordion">
  <button type="button" aria-expanded="false">What is included?<svg viewBox="0 0 24 24" aria-hidden="true">
      <path d="M6 9.5l6 6 6-6" />
    </svg></button>
  <div>
    <div>
      <p>Every motion, its code in four formats, and a GIF or a video of it.</p>
    </div>
  </div>
</div>
.accordion {
  width: 240px;
  overflow: hidden;
  border: 1px solid #1d1a261a;
  border-radius: 12px;
  background: #ffffff;
  color: #1d1a26;
  font-family: inherit;
}

.accordion button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 12px 14px;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.accordion svg {
  flex: none;
  width: 18px;
  height: 18px;
  fill: none;
  stroke: #d864ff;
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform 0.35s cubic-bezier(.65, 0, .35, 1);
}

.accordion>div {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.35s cubic-bezier(.65, 0, .35, 1);
}

.accordion>div>div {
  min-height: 0;
  overflow: hidden;
  visibility: hidden;
  transition: visibility 0.35s;
}

.accordion p {
  margin: 0;
  padding: 0 14px 12px;
  font-size: 13px;
  line-height: 1.5;
  opacity: .7;
}

.accordion.is-on svg {
  transform: rotate(180deg);
}

.accordion.is-on>div {
  grid-template-rows: 1fr;
}

.accordion.is-on>div>div {
  visibility: visible;
}

@media (prefers-reduced-motion:reduce) {
  .accordion, .accordion *, .accordion::before, .accordion::after, .accordion *::before, .accordion *::after {
    animation: none !important;
    transition: none !important;
  }
}
document.querySelectorAll('.accordion').forEach(el => {
  const trigger = el.querySelector('button');
  trigger.addEventListener('click', () => {
    const on = el.classList.toggle('is-on');
    trigger.setAttribute('aria-expanded', on);
  });
});

The three lines that do the work:

RuleWhy it is needed
grid-template-rows: 0fr to 1frThe animatable stand-in for height 0 to auto
min-height: 0 on the childA grid item will not shrink below its content without it, so 0fr would stay open
overflow: hidden on the childHides the content while the row is shorter than it

The visibility transition is the detail most copies leave out. Without it, a closed panel is invisible but its links can still be reached with Tab, and a keyboard user lands on something they cannot see. visibility switches at the end of the closing transition and at the start of the opening one, so the animation still shows.

The new way: details and interpolate-size

The <details> element opens and closes by itself, without JavaScript, and two newer CSS features let it animate:

<details>
  <summary>What is included?</summary>
  <p>Every motion, its code in four formats, and a GIF or a video of it.</p>
</details>
details {
  interpolate-size: allow-keywords;
}

details::details-content {
  block-size: 0;
  overflow: hidden;
  transition: block-size 0.35s, content-visibility 0.35s allow-discrete;
}

details[open]::details-content {
  block-size: auto;
}

::details-content is the part of <details> that opens and closes; MDN marks it Baseline 2025, newly available in current browsers. interpolate-size: allow-keywords is what allows the animation to auto, and MDN still lists it as limited availability. In Chrome 153 the panel animates; Firefox 155, which does not support interpolate-size yet, opens and closes it at once. That is a fine fallback: nothing breaks, it just does not move.

What about the max-height hack?

The old workaround animates max-height from 0 to a number bigger than the content, such as 500px. It works, badly: the transition runs over the whole 500px, so a 100px panel opens in a fraction of the time you set and closes after a pause while the invisible extra height runs out. If the content ever grows past your guess, it gets cut off. The grid trick has none of these problems.

Which one should you use?

WayBrowsersJavaScriptUse it when
Grid rows 0fr to 1frAll current onesA few lines to toggleYou want the animation everywhere today
<details> with ::details-contentAll current ones open it; the animation needs interpolate-sizeNoneAn FAQ where the animation is a bonus
max-heightAllA few linesNever, if you can avoid it

Where can you check this?

Can you change the colours and timing without editing CSS?

Motion Studio has the Accordion with its chevron colour, question, duration and easing as settings, among 150 motion graphics. It exports the result as HTML and CSS, React, Vue or Svelte, a GIF or a video, with the reduced-motion rule already in the code.

The tool

Do it in your browser.

Design

Motion Studio

150 ready-made motion graphics, from loaders and text effects to animated backgrounds, hover effects, charts and cards that follow the pointer, all playing at once: change colours, size, speed and easing, then take HTML and CSS, React, Vue or Svelte, or a GIF, a video or an animated SVG.

Keep reading

More from the blog.

Every post