---
title: "Animate Accordion Height in CSS: interpolate-size & More"
description: "Animate accordion height to auto: the grid-rows trick that works everywhere, interpolate-size, ::details-content and exclusive details groups."
url: https://frontendhelpers.com/css-accordion-animation
updated: 2026-10-12
---

# Animate accordion height in CSS: from grid rows to interpolate-size

Smooth opening and closing without guessing a max-height: a grid technique that runs in every current browser, the newer interpolate-size and ::details-content features layered over native details, and the support facts you need to choose.

CSS recipe · By [AurumCodeWorks](https://frontendhelpers.com/about) · 7 min read · Updated 12 October 2026

Standard delivery takes three to five working days. Order before 2pm for next-day delivery, or collect from any of our shops at no charge.

**Grid rows** — `grid-template-rows` from `0fr` to `1fr` on a button-driven panel; animates in every current browser.

### Returns policy

Send back any unworn item within 30 days for a full refund. Print the prepaid label from your order confirmation and drop the parcel at a post office.

**Native details** — `::details-content` plus `interpolate-size`: slides in Chrome and Edge, fades in Safari and Firefox, and simply toggles anywhere older.

### Starter

One site, shared hosting and email support during office hours.

### Team

Five sites, staging environments and a named contact.

### Agency

Unlimited sites, client billing and a phone line that answers at weekends.

**Exclusive group** — three `<details name="ac-plan">` elements: opening one closes the others, with no script.

## Why height: auto would not transition

A transition needs two values the browser can calculate between. `0` and `240px` are both lengths, so every frame in between is simple arithmetic. `auto` is not a length but an instruction to work the height out from the content, and for most of CSS’s history intrinsic keywords like it could not be interpolated. Change `height` from `0` to `auto` and the panel jumps to full size, whatever `transition` says.

Two workarounds became habits. The first transitions `max-height` to a number you hope exceeds the content, say `1000px`. The duration then covers all 1,000 pixels: a 200px panel finishes opening in the first fifth of the animation, and on closing nothing visibly moves until `max-height` drops below the content, which feels like lag. Guess too low and long content is cut off, perhaps only on a narrow phone.

The second uses JavaScript: read `scrollHeight`, set it as an explicit height, then restore `auto` on `transitionend`. Closing runs in reverse and needs a forced reflow between the two writes, or nothing animates. It works, but it is fragile, layout-thrashing code.

## The grid-rows technique: works in every current browser

CSS grid offers a cleaner route. A row sized `1fr` takes the full height of its content; a row sized `0fr` takes none. Both are the same type of value, so `grid-template-rows` can transition between them, and because nothing is guessed the timing is right for any amount of content.

```html
<div class="ac-grid">
  <button type="button" class="ac-grid-btn" aria-expanded="false" aria-controls="ac-grid-panel">Delivery options</button>
  <div class="ac-grid-panel" id="ac-grid-panel">
    <div class="ac-grid-inner">
      <div class="ac-text">Standard delivery takes three to five working days. …</div>
    </div>
  </div>
</div>
```

```css
.ac-grid-panel {
  display: grid;
  grid-template-rows: 0fr;
  visibility: hidden;
  transition: grid-template-rows 0.35s ease, visibility 0.35s;
}
.ac-grid-btn[aria-expanded="true"] + .ac-grid-panel {
  grid-template-rows: 1fr;
  visibility: visible;
}
.ac-grid-inner {
  min-height: 0;
  overflow: hidden;
}
```

```js
document.querySelectorAll('.ac-grid-btn').forEach((btn) => {
  btn.addEventListener('click', () => {
    const open = btn.getAttribute('aria-expanded') === 'true';
    btn.setAttribute('aria-expanded', String(!open));
  });
});
```

The inner wrapper is the part people forget. A grid item’s automatic minimum height is its content height, so without help the row refuses to shrink below the text. `overflow: hidden` resets that minimum to zero and clips the text; `min-height: 0` states the intent explicitly. Put padding on an element inside the wrapper, never on the wrapper itself, or it stays visible when the row is `0fr`.

The `visibility` transition is for accessibility. Clipped content is still in the page, so without it a keyboard user could tab into a link inside a closed panel. An element stays visible for the whole of a transition that has `visible` at either end, so the panel only hides once it has finished closing.

Firefox 66 was first to interpolate `grid-template-rows` (March 2019), then Safari 16 and Chrome 107 in autumn 2022. Every technique here animates layout, so the browser recalculates the page each frame; for a short panel that is cheap, and our guide to [CSS animation performance](https://frontendhelpers.com/css-animation-performance) explains when it matters.

Our [accordion animations](https://frontendhelpers.com/css-accordion-animations) collection has a paste-ready version, the [Smooth Accordion card](https://frontendhelpers.com/css-accordion-animations#anim-accordion-smooth), which drives the same grid from a checkbox and label without script. Its checkbox is hidden visually rather than with `display: none`, so it can still take keyboard focus, but screen readers announce it as a checkbox rather than an expandable section, so in production prefer a real button or native `<details>`.

## interpolate-size: animating to auto directly

Chrome and Edge 129, released in September 2024, added a property that switches on what used to be impossible. `interpolate-size: allow-keywords` lets the browser animate between a length or percentage and an intrinsic keyword: `auto`, `min-content`, `max-content`, `fit-content`, and `content` for `flex-basis`. It is inherited, so the setup recommended in Chrome’s announcement, [Animate to height: auto](https://developer.chrome.com/docs/css-ui/animate-to-height-auto), is one declaration on the root element.

```css
:root {
  interpolate-size: allow-keywords;
}
```

It is opt-in because, as [MDN’s interpolate-size reference](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/interpolate-size) explains, many existing style sheets assume keyword sizes snap, and animating them by default would break those sites. One end of the transition must be a length or percentage, so `auto` to `max-content` still jumps. Browsers that do not know the property ignore it: in October 2026 that still means Firefox and Safari, where implementation bugs are open but nothing has shipped. The demos here scope it to `.ac-details` so no other transition on the site changes.

`calc-size()` is the per-value alternative. Its first argument is the keyword; in the second, the word `size` stands for the resolved value. Such a value animates even without `interpolate-size`, and allows arithmetic that `calc()` refuses, such as `calc-size(auto, size + 2rem)`. Write a plain `height: auto` first as the fallback.

```css
.panel {
  height: 0;
  overflow: clip;
  transition: height 0.35s ease;
}
.panel.is-open {
  height: auto;                  /* other browsers: no animation */
  height: calc-size(auto, size); /* Chrome and Edge 129+ */
}
```

A percentage inside the calculation resolves against the containing block, not `size`, so scale with a multiplier such as `size * 1.5`. Support matches `interpolate-size` exactly; reach for `calc-size()` only when you need the arithmetic.

## Animating native details with ::details-content

A `<details>` element toggles instantly, and animating a wrapper div only half helps: on closing, the browser hides the content before any transition can play. The `::details-content` pseudo-element selects the part that expands and collapses, which closes that gap. [MDN’s ::details-content reference](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/::details-content) marks it Baseline 2025: Chrome and Edge 131 (November 2024), Safari 18.4 (March 2025) and Firefox 143 (September 2025).

```html
<details class="ac-details">
  <summary class="ac-summary">Returns policy</summary>
  <div class="ac-text">Send back any unworn item within 30 days for a full refund. …</div>
</details>
```

```css
.ac-details::details-content {
  opacity: 0;
  transition: opacity 0.35s ease, content-visibility 0.35s allow-discrete;
}
.ac-details[open]::details-content {
  opacity: 1;
}
@supports (interpolate-size: allow-keywords) {
  .ac-details {
    interpolate-size: allow-keywords;
  }
  .ac-details::details-content {
    height: 0;
    overflow: clip;
    transition: height 0.35s ease, opacity 0.35s ease, content-visibility 0.35s allow-discrete;
  }
  .ac-details[open]::details-content {
    height: auto;
  }
}
```

When a `<details>` closes, the browser’s own style sheet hides the content with `content-visibility: hidden`. That property is discrete, so it normally flips at once and cuts the closing animation short. The `allow-discrete` keyword, which sets `transition-behavior`, keeps the content rendered until the transition ends. Chrome’s post [More options for styling details](https://developer.chrome.com/blog/styling-details) uses the same pairing.

This is progressive enhancement in three layers. Browsers without `::details-content` drop the rules and show an ordinary `<details>`. Safari and Firefox, which have the pseudo-element but not `interpolate-size`, get the opacity fade. Chrome and Edge get the full slide. Keeping the height rules inside `@supports` means browsers that cannot animate them never apply the zero height at all.

Two gaps remain. Firefox does not yet transition `content-visibility`, so its fade plays only on opening. Safari cannot chain another pseudo-element after `::details-content`, such as `::details-content::before`.

## One open at a time: the name attribute

Give several `<details>` elements the same `name` and the browser treats them as a group: opening one closes whichever was open. The third demo is only this markup with the classes above, and no script.

```html
<div class="ac-group">
  <details class="ac-details" name="ac-plan" open>
    <summary class="ac-summary">Starter</summary>
    <div class="ac-text">One site, shared hosting and email support during office hours.</div>
  </details>
  <details class="ac-details" name="ac-plan">
    <summary class="ac-summary">Team</summary>
    <div class="ac-text">Five sites, staging environments and a named contact.</div>
  </details>
  <details class="ac-details" name="ac-plan">
    <summary class="ac-summary">Agency</summary>
    <div class="ac-text">Unlimited sites, client billing and a phone line that answers at weekends.</div>
  </details>
</div>
```

Chrome and Edge 120 and Safari 17.2 shipped this in December 2023, and Firefox 130 followed in September 2024. Older browsers ignore the attribute and let every item open independently, a perfectly usable fallback. Grouped elements need not be siblings, and if several carry `open` in the HTML, only the first in source order renders open.

Use it sparingly. An exclusive group suits short lists such as settings sections or the plans in the demo. In FAQs readers often want to compare two answers, which is why the FAQ on this page lets several stay open.

## Why native details beats a div and a script

The grid demo needed a button, `aria-expanded`, `aria-controls` and a click handler, and still lacks things `<details>` provides free. A `<summary>` is focusable, toggles with Enter or Space, and is exposed to assistive technology with an expanded or collapsed state, so screen readers announce it without any ARIA. The usual accordion bugs, such as a stale `aria-expanded` or a focusable link inside a closed panel, come from reproducing that by hand. Our [accessible website guide](https://frontendhelpers.com/accessible-website-guide) covers the wider checklist.

Find in page is the less obvious win. Chrome has opened a closed `<details>` when a find-in-page match is inside it since version 97, Safari since 26.2 (December 2025) and Firefox since 148 (February 2026), though Safari does not yet scroll the match into view correctly. Text in the grid demo’s closed panel cannot be found at all.

For a custom panel, `hidden="until-found"` restores searching: when a match is found the browser removes the attribute and fires a `beforematch` event, which your script should use to update `aria-expanded`. It works in Chrome and Edge 102, Firefox 148 and Safari 26.2; other browsers treat it as plain `hidden`.

## Browser support in October 2026

Versions come from MDN’s browser compatibility data as published on 8 October 2026; Edge uses Chrome’s version numbers. Every fallback leaves the content readable.

| Technique | Chrome/Edge | Safari | Firefox | Fallback behaviour |
| --- | --- | --- | --- | --- |
| `grid-template-rows` 0fr → 1fr | 107 (Oct 2022) | 16 (Sep 2022) | 66 (Mar 2019) | Panel opens and closes instantly |
| `interpolate-size` and `calc-size()` | 129 (Sep 2024) | Not supported | Not supported | No height animation; the final height applies at once |
| `::details-content` | 131 (Nov 2024) | 18.4 (Mar 2025) | 143 (Sep 2025) | Rules ignored; a plain details that toggles instantly |
| `content-visibility` with `allow-discrete` | 117 (Sep 2023) | 18 (Sep 2024) | Not supported | Content hides as closing starts, so no closing animation |
| `<details name>` exclusive group | 120 (Dec 2023) | 17.2 (Dec 2023) | 130 (Sep 2024) | Items open independently |
| Find in page opens details; `hidden="until-found"` | 97 (Jan 2022); 102 (May 2022) | 26.2 (Dec 2025), match not scrolled into view | 148 (Feb 2026) | Closed content is not searchable; until-found acts as `hidden` |

## Respect reduced motion

An accordion’s slide is not essential motion: the content arrives either way, so the right response to `prefers-reduced-motion` is to remove the height animation altogether and let panels open at full height in one frame. A short opacity fade on `::details-content` is a fair replacement, because it signals that something changed without moving anything across the screen.

```css
@media (prefers-reduced-motion: reduce) {
  .ac-grid-panel {
    transition: none;
  }
  .ac-details::details-content {
    transition: opacity 0.2s ease;
  }
}
```

These rules sit at the end of the style sheet so they beat the transition inside `@supports`, which has the same specificity. `content-visibility` leaves the list too: with the height snapping to zero there is nothing left to fade on closing.

## Frequently asked questions

### Can I transition height to auto in CSS now?

In Chrome and Edge 129 and later, yes: set interpolate-size: allow-keywords on :root, or write the open height as calc-size(auto, size), and a transition from height: 0 to height: auto animates. Firefox and Safari did not support either feature as of October 2026, so they jump straight to the final height. For an animation that works everywhere today, transition grid-template-rows from 0fr to 1fr.

### Why does my grid-template-rows accordion not collapse fully?

The grid item inside the animated container keeps a minimum height equal to its content. Give that inner wrapper overflow: hidden and min-height: 0, and move any padding onto an element inside it, because padding on the wrapper itself stays visible when the row is 0fr.

### How do I make only one details element open at a time?

Give every details element in the group the same name attribute, such as name="faq". Opening one closes the others, with no JavaScript. It works in Chrome and Edge 120, Safari 17.2 and Firefox 130 onwards; older browsers ignore it and let each item open independently.

### Should accordions animate when prefers-reduced-motion is set?

Not their height. Content that opens instantly loses nothing, so remove the height or grid-template-rows transition inside a prefers-reduced-motion: reduce media query. A brief opacity fade is an acceptable replacement because it signals the change without moving anything across the screen.

## Keep going

- [CSS accordion animations](https://frontendhelpers.com/css-accordion-animations): Smooth panels, rotating chevrons, content fades and plus-minus icons, each with code to copy.
 
- [Accessible website guide](https://frontendhelpers.com/accessible-website-guide): Keyboard access, focus, labels and contrast: the checks every interactive component should pass.
 
- [CSS animation performance](https://frontendhelpers.com/css-animation-performance): Why layout animations cost more than transform and opacity, and how to keep them smooth.
