GODRICH
#cssアニメーション#ボタン#ローディング#トランジション#スクロール#ダッシュボード#実用ui#デザイントレンド

CSS アコーディオン アニメーション 9選 — JS最小・CSSだけ

css アコーディオン アニメーション は、クリックした項目だけが高さごと開閉するUI部品を、JavaScriptなしでCSSだけで動かす技術です。この記事では9種類を選び、interpolate-size と calc-size() という2つの仕組みだけで実装しました。

自動再生 · タイルを押すとその項目へ · すべて1つのzip

並び順は効果の種類ではなく、実際にアコーディオンを組み立てるときの手順です。まず高さの開閉そのものを動かし(01)、次に一度に1つだけ開く制御を足し(02)、そこへ矢印や本文の登場、強調表示を重ね(03〜05)、最後に入れ子・フォールバック・特別な演出・別の場面への転用という応用を並べました(06〜09)。HTMLとCSSだけでアコーディオンを完結させるため、JavaScriptのファイルは開閉のクラス付け替え以外に入っていません。項目ごとに要点となるコードだけを載せ、完全なファイルはzipの中です。 09のサイドバーのように開閉がナビゲーションへ移ると、アイコン側も一緒に変わる必要が出てきます。その部分はcss ハンバーガーメニュー アニメーション9選にあり、下へ伸びるのではなく浮いて重なる形はCSS ドロップダウン メニュー アニメーション 9選が扱っています。

01interpolate-sizeでheight:auto

動きの内容

「開く」ボタンを押すと、閉じていたパネルが interpolate-size: allow-keywords と calc-size(auto, size) の組み合わせだけで、内容の高さまで滑らかに伸びます。

interpolate-sizeでheight:auto
新しいタブで開く

01 · interpolate-sizeでheight:auto — 計測 累積面積 11.691% · 動いたフレーム 17/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

300ms느리게 600msallow-keywords

アコーディオン メニュー css アニメーションの使いどころ

JavaScriptで本文の実際の高さを測る必要がなく、FAQ一覧や規約の詳細表示に向いています。

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

.accordion.is-open .accordion__panel {
  height: calc-size(auto, size);
  transition: height 300ms cubic-bezier(.2, .8, .2, 1);
}

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

02name属性で1つだけ開く

どんな動きか

他の項目を開くと、直前に開いていた項目が自動的に閉じます。

name属性で1つだけ開く
新しいタブで開く

02 · name属性で1つだけ開く — 計測 累積面積 32.087% · 動いたフレーム 11/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

name 속성닫힘 300ms라디오형

使いどころ

ネイティブの <details> 要素にも同じ動きをする name 属性がありますが、このデモでは開閉の速度と曲線を自分で決めたいため、あえてそれを使わずクラスの付け替えで同じ挙動を再現しています。

accs.forEach(function (acc) {
  var header = acc.querySelector('.accordion__header');
  header.addEventListener('click', function () {
    var willOpen = !acc.classList.contains('is-open');
    accs.forEach(function (other) {
      other.classList.remove('is-open');
      other.querySelector('.accordion__header')
        .setAttribute('aria-expanded', 'false');
    });
    if (willOpen) {
      acc.classList.add('is-open');
      header.setAttribute('aria-expanded', 'true');
    }
  });
});

03矢印マーカー回転

何が起きるか

ヘッダー右側の矢印が、開閉に合わせて45度から-135度まで180度回転し、今どちらの状態かを一目で伝えます。

矢印マーカー回転
新しいタブで開く

03 · 矢印マーカー回転 — 計測 累積面積 18.795% · 動いたフレーム 19/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

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

向いている場面

回転だけでなく currentColor を使っているので、見出しの文字色を変えれば矢印の色も自動でそろいます。

.accordion__chevron {
  flex: none;
  width: 9px;
  height: 9px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
}

.accordion.is-open .accordion__chevron {
  transform: rotate(-135deg);
  transition: transform 300ms cubic-bezier(.2, .8, .2, 1);
}

04本文がフェード+スライド

動きの内容

パネルが開くと、本文が上へ8px浮いた位置からフェードインしながら本来の位置に収まります。

本文がフェード+スライド
新しいタブで開く

04 · 本文がフェード+スライド — 計測 累積面積 18.717% · 動いたフレーム 19/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

8px200ms늦게 시작

使う場面

パネルの高さが伸びる動きより本文の登場を少し遅らせて重ねているため、二段階で開いた印象になります。

.accordion__body {
  opacity: 0;
  transform: translateY(-8px);
}

.accordion.is-open .accordion__body {
  opacity: 1;
  transform: translateY(0);
  transition:
    opacity 200ms ease-out 80ms,
    transform 200ms ease-out 80ms;
}

05開いた項目を強調

どんな動きか

開いている項目だけ、左側に3px幅の色付き枠と背景色が付き、リストの中でどれが開いているかすぐ分かります。

開いた項目を強調
新しいタブで開く

05 · 開いた項目を強調 — 計測 累積面積 12.473% · 動いたフレーム 17/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

3px배경 포함색만

使いどころ

枠線だけを目立たせる設定と、背景色も一緒に変える設定の2通りを選べます。

.accordion {
  border-left: 3px solid transparent;
}

.accordion.is-open {
  border-left-color: #2f6df6;
  background: #fff7e6;
  transition: border-color 300ms cubic-bezier(.2, .8, .2, 1);
}

06入れ子アコーディオン

何が起きるか

小さな入れ子のアコーディオンが親パネルの中に入っていて、親が開いている間だけ操作できます。

入れ子アコーディオン
新しいタブで開く

06 · 入れ子アコーディオン — 計測 累積面積 37.072% · 動いたフレーム 20/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

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

向いている場面

親が開いてから少し遅れて子が開き、親が閉じ始める前に子が先に閉じるよう、開始と終了の区間を親より内側にずらしています。

.accordion--inner {
  margin: 4px 20px 12px;
  background: rgba(255, 255, 255, .08);
  border-radius: 12px;
}

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

.accordion__panel--inner.is-open {
  height: calc-size(auto, size);
  transition: height 220ms ease-out;
}

07calc-size()フォールバック

動きの内容

最初のブロックは calc-size(auto, size) を使う本来の書き方で、@supports not のブロックが対応していないブラウザ向けの固定高さの代わりです。

calc-size()フォールバック
新しいタブで開く

07 · calc-size()フォールバック — 計測 累積面積 18.438% · 動いたフレーム 17/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

@supportsheight: auto 폴백auto 보간

使う場面

MDN の calc-size() 対応表 にあるとおり、interpolate-size と calc-size() は2026年9月時点でChrome 129以降だけが対応し、SafariとFirefoxの安定版はまだ対応していません。

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

.accordion.is-open .accordion__panel {
  height: calc-size(auto, size);
  transition: height 300ms cubic-bezier(.2, .8, .2, 1);
}

@supports not (height: calc-size(auto, size)) {
  .accordion.is-open .accordion__panel {
    height: 220px;
    transition: height 300ms cubic-bezier(.2, .8, .2, 1);
  }
}

08弾性バウンスで開く

どんな動きか

パネルが開くとき、中の本文がいったん1.06倍まで軽く膨らんでから元の大きさに戻り、弾んだような着地になります。

弾性バウンスで開く
新しいタブで開く

08 · 弾性バウンスで開く — 計測 累積面積 19.022% · 動いたフレーム 15/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

overshoot 6%cubic-bezier닫힘은 즉시

使いどころ

高さの伸び自体は他の項目と同じ曲線なので、弾む動きは本文のスケールだけが担っています。

.accordion.is-open .accordion__body {
  animation: bounce-in 340ms cubic-bezier(.2, 1.4, .4, 1) both;
}

@keyframes bounce-in {
  0%   { transform: scale(1); opacity: 0; }
  60%  { transform: scale(1.06); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

09サイドバーメニューに転用

何が起きるか

同じヘッダーとパネルの組み合わせを、サイドバーメニューの子項目の折りたたみにそのまま転用しました。

サイドバーメニューに転用
新しいタブで開く

09 · サイドバーメニューに転用 — 計測 累積面積 20.065% · 動いたフレーム 18/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

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

向いている場面

矢印アイコンと interpolate-size の書き方は01と共通で、違うのは横幅とインデントの数値だけです。

.sidebar__list {
  list-style: none;
  margin: 0;
  padding: 0 18px 10px 32px;
  height: 0;
  overflow: hidden;
  interpolate-size: allow-keywords;
}

.sidebar__item.is-open .sidebar__list {
  height: calc-size(auto, size);
  transition: height 220ms ease-out;
}

どこで崩れるか — 落とし穴

固定値のフォールバック

9つとも interpolate-size: allow-keywords と calc-size(auto, size) の2つだけでパネルの高さを動かしていて、JavaScriptで本文の高さを測るコードは1行もありません。1つ目の落とし穴は07のフォールバックで最初に踏んだもので、対応していないブラウザ向けに height: 220px のような固定値を書くと、本文の文字量が増えて実際の高さが変わってもフォールバックの数値だけ古いままになります。 長いFAQをこの形で畳んでも、いくつか開けばページは結局縦に長くなります。上へ戻る導線はスクロールだけで現れる形が向いていて、トップへ戻るボタン css アニメーション9選にまとめてあります。

入れ子の閉じる順番

2つ目は06の入れ子アコーディオンを組み立てたときのことです。親のパネルと子のパネルに同じ開閉区間を使ったら、親が閉じ始めても子だけ開いたまま残り、閉じる順番がそろわず二重に閉じているように見えました。子の開始と終了を親より内側の区間へ収め、子が閉じ切ってから親が閉じ始めるよう区間をずらしたところ、この崩れは直りました。修正済みの9ファイルを開くには 34fn5mpm という文字列が必要です。下のカードからzipを取ってください。

details の name 属性

3つ目はネイティブの <details> 要素です。name 属性を使えば02のような排他開閉はCSSアニメーションなしでもブラウザ標準で動きますが、MDN の details 要素の対応表 を見ると name 属性が対応しているのはChrome 120以降・Safari 17.2以降・Firefox 130以降で、対応前のブラウザでは属性そのものが無視され、複数の項目が同時に開いたままになります。zipのファイルはすべて <details> ではなくクラスの付け替えで実装しているため、この非対応の影響は受けません。

アクセシビリティ

動きを減らす設定のとき

9つとも prefers-reduced-motion: reduce で animation: none と transition: none になり、開閉の動きが止まります。デモ用のパネルだけは height: auto を残して開いた状態を表示し、情報そのものは隠しません。

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

css ドロップダウン メニューなどアコーディオン以外のCSSアニメーションは CSSアニメーションのカテゴリー に、このサイトについては 紹介ページ にまとめています。

FAQ

アコーディオンリストとして9つを同じページに全部置いても問題ありませんか?

問題ありません。02の排他開閉はグループ単位なので、ページ内に複数のアコーディオングループがあっても、それぞれのグループの中でだけ1つが開きます。06の入れ子だけは親と子で開閉の区間が重なりますが、同じページに何組置いても仕組みは崩れません。

Reactではクラスの付け外しの代わりに何を使いますか?

zipの react/ フォルダの各コンポーネントは、開閉状態を1つのbooleanのstateで持ち、is-open に相当するクラス名をそのstateから付け外ししているだけです。高さの計算とアニメーションのタイミングはこれまでと同じCSSの interpolate-size と calc-size() が担うので、JavaScript側に高さを測るコードはありません。 05の「今開いている項目」を枠で示すやり方をカード一覧にも持ち込みたい場合は、同じ合図をホバーで扱ったCSS カード ホバー アニメーション9選が参考になります。

スマホでも同じように開閉しますか?

はい、タップで同じように開閉します。ただしcalc-size()に対応していないiOSのSafariでは、07のフォールバックが働いて height: auto へ一瞬で切り替わり、なめらかに伸びる動きにはなりません。09のサイドバー転用だけは横幅がスマホの画面幅に合わせてclampで縮むため、矢印とテキストの間隔が詰まります。

解凍パスワードを入力してください

パスワードはこの記事の本文の中にあります。読み進めると出てきます。