記事を検索

details/summaryでアコーディオンの開閉アニメーションを実装する方法

昔はアクセシビリティに配慮してアコーディオンの開閉機能を実装するにはJavaScriptが必須でしたが、HTML5で導入されたdetailsタグとsummaryタグを使えば、JavaScriptなしでアクセシブルなアコーディオンを実装できるようになりました。

このページでは、そのdetails/summaryタグを用いてアコーディオンを実装する方法を紹介します。

また、開閉時のアニメーションをつける方法についても、以下の実装例についてそれぞれ解説します。

  1. JSを使わずにCSSのみでアニメーションを実装する
  2. JSを併用し、grid-template-rowsをアニメーションさせる
  3. JSを併用し、heightをアニメーションさせる
  4. (おまけ) jQueryを使ってアニメーションを実装する

details/summary要素についての基礎知識

まずは基本的な情報をおさらいしておきます。

details/summary要素には開閉機能がネイティブで備わっており、JavaScriptが必要ありません。 また、CSSでスタイリングしなくてもデフォルトで左側に三角形のマーカー(▶)が表示され、展開時には(▼へと)方向が変わるようになっており、HTMLだけでも最低限の機能と見た目が担保されています

まずはシンプルな例を見てみましょう。

Demo Preview
クリックして詳細を表示できます

ここに詳細な内容が入ります。

<details>
<summary>クリックして詳細を表示できます</summary>
<p>ここに詳細な内容が入ります。</p>
</details>

これだけで最低限の機能を持つアコーディオンが実装できます。

summary要素は、details要素の最初の子要素として配置する必要があります。

details/summary を使うメリット
  • 開閉機能がネイティブで備わっている。
  • Tabキーでフォーカス移動、Enter/Spaceキーで開閉が可能。
  • スクリーンリーダー対応で、「折りたたみ」「展開済み」などの状態が自動的に通知される。
  • ページ内テキスト検索時に、コンテンツが隠れていても自動で展開してくれる。
  • name属性を使うだけで、展開する要素を一つに制限してくれる。

独自にdiv要素やbutton要素でアコーディオンを実装する場合、これらのアクセシビリティ対応を自前で書く必要があり、JavaScriptも必要になってきます。

details/summaryを使えばその手間が省けるので、より楽に実装できます。

[open]属性について

details要素が開いている状態では、HTML上にopen属性が自動的に付与されます。これを属性セレクタで参照することで、開閉状態に応じたスタイルを適用できます。

details[open] {
/* details要素が開いている時のスタイル */
}

逆に、折りたたまれる時にはこのopen属性が自動的に削除されます。

open属性が外れている時、コンテンツはレンダリングから除外されています(content-visibility: hiddenな状態)。

クリック時には、e.preventDefault() でこのopen属性の自動付け外しを無効化することもできますが、ページ内検索時や、name属性による自動折りたたみ時に発火するのはopen属性の操作が終わった直後に発火するtoggleイベントとなることに注意が必要です。

デフォルトの三角形マーカーを非表示にする方法

summary要素の三角形マーカーは、Chrome/Edge/Firefoxなどでは、summary要素のdisplayプロパティをlist-item以外に変更することで自動的に非表示になります。

Safariでは、::-webkit-details-markerという独自の疑似要素でこのマーカーがスタイリングされているため、以下のようにして明示的に非表示にするスタイルを書く必要があります。

summary::-webkit-details-marker {
display: none;
}

summaryタグのアクセシビリティに関する問題点

一部のスクリーンリーダーとブラウザの組み合わせによっては、summary内の見出しロールが検出できない問題があるようです。 これはsummaryが暗黙的にrole="button"扱いになっていることが原因のようです。

HTMLの仕様としては summary > hタグ の構造は許可されているものの、一部の環境ではスクリーンリーダーでは正しく反映されない状況となっています。

今はほとんど解決してるっぽい…??

この問題について、2026年1月に色々試してみましたが、おそらくですがほとんど解消してるのではないかと思います。
Safari + VoiceOver の組み合わせで特に問題が多かったようですが、MacOS 15.7 / iOS 26.1 で確認した限りではローターの見出しリストにもちゃんと出て来ました。

 

まずはシンプルに実装してみる

それでは、実際にdetailssummaryを使ってアコーディオンを実装してみましょう。

まずは、開閉時のアニメーションはなしにして、アイコン部分だけ軽くアニメーションさせてみます。

シンプルな details/summary のアコーディオン
別タブで表示
@import url(../_common.css);

/* 開いている時 */
.accordion[open]{
	--icon-transform: rotate(90deg);
}

/* コンテンツ部分の padding */
.accordion__body {
	padding: 0.5rem 0.75rem 1.25rem;
}

HTMLの構造はシンプルです。.accordionsというラッパー要素の中に、複数のdetails要素を配置しています。各details要素にはsummaryでタイトルを、その後に続くdiv要素の中ではコンテンツを配置しています。

アニメーションをつけなくて良い場合は、なんとこれだけでアコーディオンが実装できるようになっています。

コンテンツの開閉アニメーション

アコーディオンUIを実装する際、コンテンツ部分の開閉にスライドアニメーションを付けるケースが多いですが、これまで、CSSだけではdetailsの開閉アニメーションを実装することは困難でした

従来の問題は次の2点です。

  1. height: auto ではアニメーションができない。
  2. open属性が外れている状態からのアニメーションができない。(display:none からフェードインできないのと同じ)

しかしながら、最近ではCSSだけでもアニメーションが可能になりつつあります。

  • 1を解決するTips : grid-template-rows0fr~1frへトランジションさせるというテクニックがあります。また、将来的にはinterpolate-size: allow-keywords;を使うことでheight:autoのアニメーションが可能になります。
  • 2を解決するTips : ::details-content擬似要素を使うことで、コンテンツ用のslot要素に対してスタイルを付与することができるようになり、アニメーションも可能になりました。

interpolate-size, ::details-contentはまだブラウザ対応が完全ではないため積極的に導入はしずらいですが、将来的にはこれらを使ってCSSだけで開閉アニメーションを実装できるようになります。

ブラウザサポート状況(2025年12月時点)

Can Use ? interpolate-size Chrome 129 (2024/9)~ Safari Firefox
Can Use ? ::details-content Chrome 131 (2024/11)~ Safari 18.4 (2025/3)~ Firefox 143 (2025/9)~

これを見ると、::details-contentは主要ブラウザで広くサポートされはじめていることがわかります。

アニメーション実装例1: CSSだけで開閉アニメーションを実装する例

::details-content に対して 0fr~1frのトランジションTispを使い、CSSだけで開閉アニメーションを実装する例を紹介します。

CSSのみでアニメーションを実装する例
別タブで表示
@import url(../_common.css);

/*  ::details-content をサポートしていないブラウザではスタイルが崩れないようにアニメーション関連のみにする */
.accordion::details-content {
	display: grid;
	transition-duration: var(--duration);
	transition-property: content-visibility, grid-template;
	transition-behavior: allow-discrete;

	/* 閉じてる状態 */
	grid-template-rows: 0fr;
}

/* 開いている時 */
.accordion[open]{
	--icon-transform: rotate(90deg);
}
.accordion[open]::details-content {
	grid-template-rows: 1fr;
}
.accordion__body {
	/* grid-template のアニメーションに必要 */
	overflow: hidden;
}
.accordion__content {
	padding: 0.5rem 0.75rem 1.25rem;
}
ポイント
  • ::details-contentに対してgrid-template-rows0fr~1frへトランジションさせる.
    • transition-property: content-visibility & transition-behavior: allow-discreteの指定を忘れないように.
    • また、その子要素に対してoverflow: hiddenが必要なので、div(.accordion__body)でラップする.
    • ::details-contentを使うのは、まだあくまでアニメーション部分だけ(非サポートブラウザではただアニメーションがなくなるだけですむように。)
  • コンテンツ上下にpaddingをつける時は、さらにネストを重ねたコンテンツラッパーの.accordion__contentにつける。(もしくは、.accordion__bodyの擬似要素を使ってスペースを確保する。)

Can I use でcontent-visibility: Transitionable when setting transition-behavior: allow-discreteを確認するとFirefoxではサポートされていないことになっていますが、手元のFirefox v.145.0.2 では動作していました。

コンテンツ上下のpadding/marginの付け方について

上記では、コンテンツの余白(padding)をつけるために、.accordion__bodyの直下でさらに.accordion__contentを配置しています。
これはなんでかというと、.accordion__bodyに上下方向の余白(paddingmargin)をつけると、アニメーションが一瞬ちらつくためです。

元々、grid-template-rows を 0fr1fr へアニメーションさせる時、その要素またはその直下要素に対して上下方向の余白があると、正常に動作しません。

それ自体は、次のようにpadding-blockをtransitionでアニメーションさせることで回避できます。

/* Memo: .accordion__contentの上下にpaddingをつける場合 */
.accordion__content {
overflow: hidden;
padding-inline: 0.5em;
/* 上下方向はアニメーションさせる */
transition: padding-block var(--duration);
transition-duration: var(--duration);
}
/* 開いている間のみ余白をつける */
[open] > .accordion__content {
padding-bottom: 1em;
}

ただし、::details-contentを使っている場合は、このようにアニメーションさせて対策しても、初回アニメーション時にのみ、1フレームだけちらつきが発生してしまうようです。(Mac Chromeで確認)

なので、::details-content0fr1frでアニメーションさせる場合は、余白をつけるためにさらに階層を深くするか、擬似要素やスペーサー要素を使って余白を確保することでその問題を回避する必要があります。

アニメーション実装例2: JSを併用し、grid-template-rowsをアニメーションさせる

Safariもサポートしつつアニメーションを実装するなら、JSの力を借りましょう。

スライドアニメーションは先ほどと同じ0fr~1frへのトランジションを使いますが、アニメーショントリガー用のdata-opened属性をJSでつけ外しするような実装をする例を紹介します。

ポイントは、open属性の切り替わりとはタイミングをずらして独自のdata属性をつけ外しすることです。

grid-template-rowsでアニメーションする例
別タブで表示
@import url(../_common.css);

.accordion__body {
	display: grid;
	transition-property: padding-block, grid-template;
	transition-duration: var(--duration);

	/* 閉じてる状態 */
	grid-template-rows: 0fr;
	padding-block: 0;
	padding-inline: 0.75rem;
}

/* 開いている時 */
[data-opened] > .accordion__body {
	grid-template-rows: 1fr;
	padding-block: .5rem 1.25rem;
}

/* grid アニメーションに 必須 */
.accordion__content {
	overflow: hidden;
}

この実装の問題点
  • name属性を使って同一グループ内でのアコーディオン開閉を一つに制限すると、アニメーションがうまく動作しなくなります。

これについては、上の例では、data-multipleという属性を使って独自実装することで対応しています

また、iframeの埋め込み内だとページ内検索時の展開アニメーションが動作しないように見えますが、別タブで確認すると問題なくアニメーションします。

アニメーション実装例3: JSを併用し、heightをアニメーションさせる(連打可能)

最後に、heightをアニメーションさせる実装例を紹介します。

先ほどと同様、jsでdata-opened属性を付け外ししながら、heightを取得してそれをCSS変数へセットします。

アニメーション自体は、CSS側でheightのtransitionで行います。

heightでアニメーションさせることで、クリックを連打される(開いている途中にクリックされたりする)場合でも、すぐにその地点のから逆方向へ動作するようにしています。

heightでアニメーションする例
別タブで表示
@import url(../_common.css);

.accordion {
	overflow: hidden; /* アニメーション中にコンテンツがはみ出ないように */
	position: relative;

	height: var(--js--height);
	transition: height var(--duration);

	/* border や padding があるときに高さがズレるのを防ぐ */
	box-sizing: content-box;

	/* jsで変数にセットする */
	--js--height: auto;
}

.accordion__body {
	padding: .5rem .75rem 1.25rem;
}

/* パネルが完全に閉じている時、absoluteで飛ばしておくとページ内検索の挙動が少し良くなる (なぜかはよくわかってない) */
.accordion:not([data-opened]) > .accordion__body{
	position: absolute;
}

/* フォーカス時のアウトラインを少し内側に寄せる( overflow:hidden によって途切れてしまうので)  */
.accordion__title:focus-visible {
	background-color: color-mix(in srgb, var(--bdc), transparent 80%);

	/* offset 調整だけだとブラウザで差が大きいので、スタイルまで指定してしまう */
	outline: solid 2px currentColor;
	outline-color: revert;
	outline-offset: -3px;
}

grid-template-rowsの例と今回のheightの例を比べると、少し動きのニュアンスに違いがあることに気づきますでしょうか。

アニメーション実装例2 ではpaddingも同時に操作していたので、動きが少し異なります。
(grid-template-rowsの場合もさらに階層深くするか擬似要素でスペース確保すれば同様の動きになります。)

この実装の問題点
  1. name属性を使って同一グループ内でのアコーディオン開閉を一つに制限すると、アニメーションがうまく動作しなくなります。

これついては アニメーション実装例2 と同じように、独自に用意した data-multiple 属性を使って対応しています。

(おまけ)jQuery を使ってアニメーションを実装する例

最後に、一応軽くjQuery実装パターンも紹介しておきます。
どうしても要件的にjQueryを使わなければならないような場合のみ、参考にしてみてください。

個人的には、アコーディオンのためだけにjQueryを導入するなんてことは非推奨です。(slideUp/slideDownは便利ですけど…。)

jQueryでアニメーションを実装する例
別タブで表示
@import url(../_common.css);


.accordion__body {
	padding: 0.5rem 0.75rem 1.25rem;
}
  • 他の実装例ではちゃんとしていた、data-multiple属性の独自実装などはこのjQueryの例ではしていません。
  • jQueryのこの実装では、ページ内検索時の展開アニメーションは動作しません。

まとめ

details/summary要素を使えば、開閉機能・キーボード操作・スクリーンリーダー対応・ページ内検索時の自動展開といったアクセシビリティ要件を、JavaScriptなしで満たせます。

開閉時のアニメーションについては、::details-contentgrid-template-rows: 0fr → 1frのトランジションを使えば、CSSだけでも実装できるようになってきています。
さらに将来的にはinterpolate-sizeによる height:auto のアニメーションのブラウザサポートも採用できるようになるでしょう。

ただし、より広いブラウザ対応が必要な場合は、現在はまだJavaScriptとの併用が現実的です。

アニメーションはなくても困るものではないため、無理に広範囲のブラウザをサポートする必要はなく、CSSだけで実装してモダンブラウザのみアニメーションされる状態にしておくのも許容範囲だと個人的には思います。

参考リンク

この記事を執筆するにあたり、いろんな記事を参考にさせていただきました。

CSSでheight: autoでもアニメーションが可能に! interpolate-sizeとは - ICS MEDIA UIのインタラクションの実装で、height: 0 → autoなど、数値とキーワード値とをアニメーションさせたいと思ったことはないでしょうか。一見可能そうに見えるものの、従来はCSSのみではアニメーションが不可能でした。
ICS MEDIA
detailsとsummaryタグで作るアコーディオンUI - アニメーションのより良い実装方法 - ICS MEDIA アコーディオン型ユーザーインターフェイス(UI)はウェブページでよくみられる表現です。巷ではさまざまな方法でアコーディオンUIを作る方法が紹介されていますが、みなさんはどのような方法で実装していますか。
ICS MEDIA
CSSのみでdetails要素のアニメーションを実装する方法 &#8211; TAKLOG
www.tak-dcxi.com
details要素に命を吹き込む::details-content擬似要素の登場 details要素のコンテンツ部分にスタイルを適用できる::details-content擬似要素がBaseline 2025に加わりました。details要素を用いたスタイリングの守備範囲が広がっただけではなく、アニメーションの適用も容易になりました。
k8o
アクセシブルなアコーディオンの実装について考える
Zenn
【jQuery】details要素・summary要素でアコーディオン【メモ】 | deep-space.blue デモはこちら 実装 HTML(ベースの構造と動作) &lt;details class=&quot;c-accordion js-accordion&quot;&gt; &lt;summary class=&quot;c-accordion__title js-acco
deep-space.blue