GODRICH
#css-animation#button#loader#transition#scroll#dashboard#ui#design-trends

9 CSS Accordion Animations — No Library, Pure CSS

A css accordion animation expands a header's panel to full height when it opens and collapses it to zero when it closes — nine versions here, plain CSS

Auto-plays · click a tile to jump to its section · all nine in one zip

The order below follows how much control each panel hands to CSS alone. 01 through 03 are the core mechanics — how the height itself expands, how one panel closes its neighbor, how the arrow reports state. 04 through 06 dress that motion up with a content transition, a highlight, and nesting. 07 through 09 are the idea-form entries: a fallback for older browsers, a bounce borrowed from spring easing, and the same structure reused as a sidebar. Watch the grid above loop through all nine on its own — each one below is the 480×300 iframe version, running a two-second open-close cycle automatically since there is nothing inside it to click. Once the expanding thing becomes navigation, as in 09, the icon has to answer too — that's the CSS hamburger menu animations — and when a list floats over the page instead of pushing it down, the CSS dropdown menu animations cover that shape.

01height:auto via interpolate-size

The motion

The panel grows to its natural content height in one step, using interpolate-size: allow-keywords together with height: calc-size(auto, size) — the shortest path to css accordion animation dynamic height without ever reading scrollHeight in JavaScript.

height:auto via interpolate-size
Open in a new tab

01 · height:auto via interpolate-size — Measured area 11.691% · moving frames 17/23Original by GODRICH · SCSS and React ship in the zip · See the password

300ms느리게 600msallow-keywords

Where a css accordion effect fits

It's the base pattern the rest of this set builds on, useful for FAQ lists, terms-of-service details, and form help text. Support is still thin: MDN documents interpolate-size and calc-size() as shipping in Chrome 129 and missing from Safari and Firefox as of September 2026, so every demo below carries a plain height: auto fallback.

.accordion__panel {
  height: 0;
  overflow: hidden;
  interpolate-size: allow-keywords;
}

.accordion.is-open .accordion__panel {
  height: calc-size(auto, size);
  transition: height $duration $easing;
}
@supports not (height: calc-size(auto, size)) {
  .accordion.is-open .accordion__panel { height: auto; }
}

02Exclusive open via name attribute

What it does

Closing whichever panel was open the moment another opens is, natively, one HTML attribute: grouping <details> elements under a shared name turns them into a radio-style set with no script at all, though browser support only reached Chrome 120, Safari 17.2, and Firefox 130. This demo reproduces the same closing behavior with plain buttons and two panels animated on opposite phases, which is the version to reach for on projects that still support older browsers.

Exclusive open via name attribute
Open in a new tab

02 · Exclusive open via name attribute — Measured area 32.087% · moving frames 11/23Original by GODRICH · SCSS and React ship in the zip · See the password

name 속성닫힘 300ms라디오형

Where it fits

Settings menus and single-answer FAQs are the classic use.

.accordion__panel.is-demo-a { animation: accordion-loop-a $dur-loop $easing infinite; }
.accordion__panel.is-demo-b { animation: accordion-loop-b $dur-loop $easing infinite; }
@keyframes accordion-loop-a {
  0%, 4%   { height: 0; }
  20%, 46% { height: calc-size(auto, size); }
  62%, 100% { height: 0; }
}
@keyframes accordion-loop-b {
  0%, 54%  { height: 0; }
  70%, 96% { height: calc-size(auto, size); }
  100%      { height: 0; }
}

03Chevron marker rotate

What you see

The header's chevron rotates 180 degrees exactly when the panel does, driven by transform: rotate() on the same class toggle instead of swapping between two separate icons.

Chevron marker rotate
Open in a new tab

03 · Chevron marker rotate — Measured area 18.795% · moving frames 19/23Original by GODRICH · SCSS and React ship in the zip · See the password

180deg머리 오른쪽색 변화 추가

Where it fits

It's a small signal, but a header without it reads as unresponsive for the half-second before the panel visibly moves. Reach for it on accordion headers and dropdown menus, anywhere the trigger itself needs to show which way it's facing.

.accordion__chevron.is-demo {
  animation: chevron-loop $dur-loop $easing infinite;
}
@keyframes chevron-loop {
  0%, 8%    { transform: rotate(45deg); }
  42%, 58%  { transform: rotate(-135deg); }
  92%, 100% { transform: rotate(45deg); }
}

04Content fade + slide

The motion

The paragraph fades in while sliding down about 8 pixels from above, timed slightly behind the panel's own height change so the text never smears across a box that's still growing.

Content fade + slide
Open in a new tab

04 · Content fade + slide — Measured area 18.717% · moving frames 19/23Original by GODRICH · SCSS and React ship in the zip · See the password

8px200ms늦게 시작

Good places for it

It feels calmer than an instant paste-in, and it suits detail text and product option panels where the reveal should feel considered rather than mechanical.

.accordion__body.is-demo {
  animation: content-loop $dur-loop $easing infinite;
}
@keyframes content-loop {
  0%, 8%    { opacity: 0; transform: translateY(-8px); }
  30%, 70%  { opacity: 1; transform: translateY(0); }
  92%, 100% { opacity: 0; transform: translateY(-8px); }
}

05Open item border highlight

What it does

A 3px left border and a background shift call out whichever panel is currently open, running on the same rhythm as the height change so the highlight and the panel land together.

Open item border highlight
Open in a new tab

05 · Open item border highlight — Measured area 12.473% · moving frames 17/23Original by GODRICH · SCSS and React ship in the zip · See the password

3px배경 포함색만

When to use it

Settings lists and any UI that needs to mark "you are here" among several sections benefit from a cue that doesn't rely on height alone to be noticed.

.accordion.is-demo-border {
  animation: border-loop $dur-loop $easing infinite;
}
@keyframes border-loop {
  0%, 8%    { border-left-color: transparent; background: $subject-cream; }
  42%, 58%  { border-left-color: $color; background: $subject-cream; }
  92%, 100% { border-left-color: transparent; background: $subject-cream; }
}

06Nested accordion

What you see

A second, smaller accordion sits inside the outer panel and only starts revealing once the outer one has mostly finished opening, so the two never compete for the same visual space at once.

Nested accordion
Open in a new tab

06 · Nested accordion — Measured area 37.072% · moving frames 20/23Original by GODRICH · SCSS and React ship in the zip · See the password

들여쓰기 16px2단계동시 허용

Where it fits

Multi-level FAQs and category tree menus are the obvious fit — anywhere a single question hides a few sub-answers underneath it.

.accordion--inner {
  margin: 4px clamp(14px, 4vw, 20px) 12px;
  background: rgba(255, 255, 255, .08);
  border-radius: $r-control;
}
.accordion__panel--inner {
  height: 0;
  overflow: hidden;
  interpolate-size: allow-keywords;
}
@keyframes accordion-loop-inner {
  0%, 20%   { height: 0; }
  32%, 46%  { height: calc-size(auto, size); }
  58%, 100% { height: 0; }
}

07calc-size() fallback pattern

The motion

Every rule that reads height: calc-size(auto, size) sits behind an @supports not (height: calc-size(auto, size)) block that swaps in a fixed pixel height instead, so a browser that doesn't understand the function still gets a transition, just a less exact one.

calc-size() fallback pattern
Open in a new tab

07 · calc-size() fallback pattern — Measured area 18.438% · moving frames 17/23Original by GODRICH · SCSS and React ship in the zip · See the password

@supportsheight: auto 폴백auto 보간

Good places for it

It's the pattern to reach for on any form that has to keep working outside Chrome, which — per the support numbers under item 01 — is still most of the field.

.accordion.is-open .accordion__panel {
  height: calc-size(auto, size);
  transition: height $duration $easing;
}
@supports not (height: calc-size(auto, size)) {
  .accordion.is-open .accordion__panel { height: auto; }
}

.accordion__panel.is-demo {
  animation: accordion-loop $dur-loop $easing infinite;
}
@supports not (height: calc-size(auto, size)) {
  @keyframes accordion-loop {
    0%, 8%   { height: 0; }
    42%, 58% { height: 220px; }
    92%, 100% { height: 0; }
  }
}

08Elastic bounce open

What it does

The panel opens along its normal height transition, but the content inside overshoots to 106% scale before easing back to 100%, so the whole thing reads as a small spring rather than a straight-line stop.

Elastic bounce open
Open in a new tab

08 · Elastic bounce open — Measured area 19.022% · moving frames 15/23Original by GODRICH · SCSS and React ship in the zip · See the password

overshoot 6%cubic-bezier닫힘은 즉시

When to use it

Use it sparingly — a promo accordion or an onboarding card that wants a beat of extra attention, not a settings list a visitor opens forty times a session.

.accordion__body.is-demo {
  transform-origin: top center;
  animation: bounce-loop $dur-loop $easing infinite;
}
@keyframes bounce-loop {
  0%, 8%    { transform: scale(1); opacity: 0; }
  36%        { transform: scale(1.06); opacity: 1; }
  50%, 58%  { transform: scale(1); opacity: 1; }
  92%, 100% { transform: scale(1); opacity: 0; }
}

09Reused as sidebar nav

What you see

The same header-plus-panel markup collapses a group of sidebar links instead of a paragraph, with no chevron and a little extra left padding so it reads as a nested list rather than a question-and-answer pair.

Reused as sidebar nav
Open in a new tab

09 · Reused as sidebar nav — Measured area 20.065% · moving frames 18/23Original by GODRICH · SCSS and React ship in the zip · See the password

들여쓰기 12px아이콘 없이항상 하나 열림

Where it fits

Dashboard sidebars and documentation tables of contents reuse it as-is — the height trick doesn't care what's sitting inside the panel.

.sidebar__list {
  list-style: none;
  margin: 0;
  padding: 0 clamp(14px, 4vw, 18px) 10px clamp(26px, 8vw, 32px);
  height: 0;
  overflow: hidden;
  interpolate-size: allow-keywords;
}
.sidebar__list.is-demo {
  animation: accordion-loop $dur-loop $easing infinite;
}

Where it breaks — the trap

Nested panels need their own overflow

An accordion animation css codepen won't warn you about this one: 06's inner accordion only opens smoothly if both panels — the inner one and the outer one wrapped around it — carry their own overflow: hidden and their own interpolate-size: allow-keywords. Drop overflow: hidden from either level and the nested panel's calc-size(auto, size) has nothing to clip against, so instead of growing on a curve it snaps straight to full height the instant the outer panel starts moving. That single missing line is the entire difference between item 06 reading as one motion and reading as two unrelated jumps. Grab the whole nested pattern from the zip instead of retyping two levels of panel by hand — the archive opens with 34fn5mpm, typed in exactly as it's written here.

A long FAQ folded into panels like these still makes for a tall page once a few are open, which is why the way back up matters — the scroll-driven version of that button is in the back to top button CSS set.

Accessibility

Reduced motion

Every one of the nine respects prefers-reduced-motion, but what gets left behind differs by item. 01, 02, 04 through 07, and 09 skip straight to the open or closed end state — no height transition, no fade, no border sweep — since a panel that still opens and closes on click loses nothing by doing it instantly. 08's bounce loses the most: the scale overshoot exists purely for delight, so reduced motion drops it to a flat opacity change with no scale at all. 03's chevron still flips instantly along with the panel, because the icon's job is to report state, not to entertain, and losing that would leave a reader guessing which way it points. MDN's guide to prefers-reduced-motion covers what triggers the query on each platform.

@media (prefers-reduced-motion: reduce) {
  .accordion__panel,
  .accordion__chevron,
  .accordion__body,
  .accordion.is-open .accordion__panel {
    animation: none !important;
    transition: none !important;
  }
}

More css accordion examples and every other CSS pattern on the site are filed in the CSS category; what GODRICH actually is — and isn't — sits on the about page.

FAQ

Do I need JavaScript to run any of these?

Only enough to toggle a class. Every open-close panel here is one click handler adding or removing is-open, and the height change, the chevron rotation, and the border highlight are all CSS transitions or animations underneath it. Item 02's exclusive-open behavior is the one exception worth naming: the script in that demo mimics what the native <details name="group"> attribute already does with no JavaScript at all, once your target browsers support it.

How does a css accordion animation dynamic height differ from the old max-height hack?

The old trick set max-height to something absurdly large, like 999px, and transitioned that instead of the panel's real height, so every accordion animated at a speed based on the guess rather than the content, and short panels felt sluggish next to tall ones. calc-size(auto, size) and interpolate-size read the panel's actual computed height and transition to that number directly, so a two-line answer and a two-hundred-line one both finish close to the same duration. That's the difference between a fixed-guess transition and a real css accordion animation dynamic height solution.

Can I drop these into a Tailwind or React project?

Yes, nothing here depends on a specific build tool. Tailwind's utility classes sit fine alongside this SCSS since the motion lives entirely in interpolate-size, calc-size(), and the keyframes rather than in any class Tailwind would also want to own, and the zip's react/ folder wraps each pattern in one component per effect. To test a single pattern before wiring it into a project, paste the vanilla SCSS into any accordion animation css codepen and toggle the class by hand. If 05's "this one is open" border is the part you want on a grid of cards rather than a stack of panels, the same signal handled on hover sits in the CSS card hover effects.

Enter the archive password

The password is inside this article. You will find it as you read.