Search articles
Translation Not Available
This page has not been translated yet. You are viewing the Japanese version.

details/summaryで実装するアコーディオン

アコーディオンUIは、限られたスペースに多くの情報を整理して表示できる便利なコンポーネントです。FAQページやサイドメニュー、設定画面など、さまざまな場面で活用されています。

昔はアコーディオンの開閉機能を実装するにはJavaScriptが必須でしたが、HTML5で導入されたdetailssummary要素を使えば、JavaScriptなしでアクセシブルなアコーディオンを実装できます。

このページでは、detailssummaryを用いたアコーディオンの構築方法を解説します。

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

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

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

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

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

このHTMLを書くだけで、以下のようになります。

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

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

details/summary を使うメリット

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

独自にdiv要素やbutton要素でアコーディオンを実装する場合、これらのアクセシビリティ対応を自前で実装する必要がありJavaScriptも必要になってきますが、details/summaryを使えばその手間が省けます。

summary内に見出しタグを配置した場合の問題点

summary内に見出しタグ(h1~h6)を配置した場合、スクリーンリーダーが見出しとして検出できなくなってしまうという問題があります。 これはsummaryが暗黙的にrole="button"扱いになっていることが原因のようで、アクセシビリティツリー上ではその中に配置されている見出しのロールを検出できません。

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

VoiceOver読み上げ時にその「三角形の選択ボタン」の読み上げがいちいち入るのが邪魔な気がする(読み上げ対象から除外する方法がわからなかった)

[open]属性

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

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

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

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

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

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

details/summaryで実装するアコーディオン

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

Demo Preview
Open in new tab ↗
/* アコーディオン全体のコンテナスタイル */
.accordions {
	--bdc: hsl(210, 16%, 84%);
	border: solid 1px var(--bdc);
	border-radius: 8px;
	overflow: clip;
}

/* 2つ目以降のアコーディオンには上部にボーダーを追加 */
.accordion + .accordion {
	border-top: solid 1px var(--bdc);
}

/* コンテンツエリアのパディング */
.accordion__content {
	padding: 1.25em;
}

/* タイトル部分のスタイル */
.accordion__title {
	display: flex; /* list-item 以外にすることでデフォルトの三角アイコンは非表示になる */
	align-items: center;
	justify-content: space-between;
	gap: 1em;
	padding: 1em 1.25em;
	cursor: pointer;
	background-color: hsl(210, 8%, 96%);

	/* Tab操作によるフォーカス時のアウトラインを少し内側に寄せる( overflow:clip で見えづらくなることへの対処)  */
	outline-offset: -1px;

	/* Safariで表示されるデフォルトの三角形アイコンを削除 */
	&::-webkit-details-marker {
		display: none;
	}
}

/* タイトルホバー時、背景色を少し濃くする */
@media (any-hover: hover) {
	.accordion__title:hover {
		background-color: hsl(210, 12%, 92%);
	}
}
/* Tabキーでのフォーカス時も同じスタイリング */
.accordion__title:focus-visible {
	background-color: hsl(210, 16%, 92%);
}

/* アイコン擬似要素 */
.accordion__title::after {
	content: '';
	display: block;
	width: 1em;
	height: 1em;
	background-color: currentColor;

	/*  clip-path で > アイコンを描画し、回転させる  */
	clip-path: polygon(
		calc(35% + 4%) calc(10% - 4%),
		calc(75% + 4% * 2) 50%,
		calc(35% + 4%) calc(90% + 4%),
		calc(35% - 4%) calc(90% - 4%),
		calc(75% - 4% * 2) 50%,
		calc(35% - 4%) calc(10% + 4%)
	);
	rotate: 90deg;
	transition: scale 0.3s;
}

/* アコーディオンが開いている時、アイコンを反転 */
.accordion[open] > .accordion__title::after {
	scale: -1 1;
}

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

コンテンツの開閉アニメーションについて

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

従来の問題は次の2点。

  1. height: auto ではアニメーションができない。
  2. open属性が外れている時、コンテンツがレンダリングから完全に除外されてしまう。(display:none からフェードインできないのと同じ)

しかしながら、最新のCSSでは、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は主要ブラウザで広くサポートされはじめていますが、interpolate-sizeはChromium系のみの対応となっています。非対応ブラウザではアニメーションなしで即座に開閉してしまいます。

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

::details-content に対して 0fr~1frのトランジションTispを使い、CSSだけで開閉アニメーションを実装する例を紹介します。 (interpolate-size: allow-keywords;を使ってheightをアニメーションさせた方が動きが滑らかな気がしたので、将来的にはそちらを使いたいですが、ひとまずサポート状況を優先しています。)

Demo Preview
Open in new tab ↗
/* ----- details ----- */

.accordions {
	--bdc: hsl(210, 16%, 84%);
	--duration: 0.3s;
	border-block: solid 1px var(--bdc);
}

.accordion {
	/* icon 変化用の変数 */
	--icon-rotate: 0deg;

	&[open] {
		--icon-rotate: 90deg;
	}

	& + & {
		border-top: 1px solid var(--bdc);
	}

	/* 
		::details-content をサポートしているブラウザでは開閉アニメーションをつける ( 非サポートブラウザでは無視される )
	*/
	&::details-content {
		display: grid;
		transition-duration: var(--duration);
		transition-property: content-visibility, grid-template;
		transition-behavior: allow-discrete;

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

	&[open]::details-content {
		/* 開いている時 */
		grid-template-rows: 1fr;
	}
}

.accordion__title {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1em;
	cursor: pointer;
	padding: 1em 0.5em;

	/* Safariで表示されるデフォルトの三角形アイコンを削除 */
	&::-webkit-details-marker {
		display: none;
	}
}

/* タイトルホバー時、背景色を少し濃くする */
@media (any-hover: hover) {
	.accordion:has(> .accordion__title:hover) {
		background-color: color-mix(in srgb, var(--bdc), transparent 60%);
	}
}
/* Tabキーでのフォーカス時も同じスタイリング */
.accordion:has(> .accordion__title:focus-visible) {
	background-color: color-mix(in srgb, var(--bdc), transparent 60%);
}

/* アイコン */
.accordion__icon {
	display: grid;
	width: 1em;
	height: 1em;
	place-items: center;

	&::before,
	&::after {
		content: '';
		display: block;
		grid-area: 1 / 1;
		background-color: currentColor;
	}
	&::before {
		width: 0.125em;
		height: 100%;
		transition: rotate var(--duration);
		rotate: var(--icon-rotate);
	}
	&::after {
		height: 0.125em;
		width: 100%;
	}
}

.accordion__content {
	padding-inline: 0.5em;
	overflow: hidden;
	transition: padding-block var(--duration);
	transition-duration: var(--duration);

	[open] > & {
		padding-bottom: 1em;
	}
}
ポイント
  • ::details-contentに対してgrid-template-rows0fr~1frへトランジションさせる.
  • transition-property: content-visibility & transition-behavior: allow-discreteの指定を忘れないように.
  • また、その子要素に対してoverflow: hiddenが必要なので、コンテンツはdiv(.accordion__content)でラップする.
  • ::details-contentを使うのは、まだあくまでアニメーション部分だけ(非サポートブラウザではただアニメーションがなくなるだけですむように。)
  • コンテンツにpaddingをつける時は.accordion__contentにつけて、open属性のない時はpadding-block0するようにする。もしくは、さらにdivでラップしてその中につける。

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

JS + CSSで開閉アニメーションを実装する例

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

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

ポイントは、open属性とタイミングをずらしてdata属性をつけ外しすることです。

Demo Preview
Open in new tab ↗
.accordions {
	--duration: 0.3s;
	--bdc: hsl(210, 16%, 84%);
	border-block: solid 1px var(--bdc);
}

.accordion {
	& + & {
		border-top: 1px solid var(--bdc);
	}
}

.accordion__title {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1em;
	cursor: pointer;
	padding: 1em 0.5em;

	/* Safariで表示されるデフォルトの三角形アイコンを削除 */
	&::-webkit-details-marker {
		display: none;
	}

	/* アイコン擬似要素 */
	&::after {
		content: '';
		display: block;
		width: 1em;
		height: 1em;
		background-color: currentColor;

		/*  clip-path で > アイコンを描画し、回転させる  */
		clip-path: polygon(
			calc(35% + 4%) calc(10% - 4%),
			calc(75% + 4% * 2) 50%,
			calc(35% + 4%) calc(90% + 4%),
			calc(35% - 4%) calc(90% - 4%),
			calc(75% - 4% * 2) 50%,
			calc(35% - 4%) calc(10% + 4%)
		);
		rotate: 90deg;
		transition: scale 0.3s;
	}

	/* アコーディオンが開いている時、アイコンを反転 */
	[data-opened] > &::after {
		scale: -1 1;
	}
}

/* タイトルホバー時、背景色を少し濃くする */
@media (any-hover: hover) {
	.accordion:has(> .accordion__title:hover) {
		background-color: color-mix(in srgb, var(--bdc), transparent 60%);
	}
}
/* Tabキーでのフォーカス時も同じスタイリング */
.accordion:has(> .accordion__title:focus-visible) {
	background-color: color-mix(in srgb, var(--bdc), transparent 60%);
}

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

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

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

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

この実装の問題点

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

なので、それが気になる場合は独自実装が必要です。 上記の例では、data-multipleという属性を使って独自実装しています。

  • さらに、ページ内検索時の開閉アニメーションも完全には動作しません。

これらの開閉動作はtoggleイベントでopen属性の切り替え後にしか検知できないためです。

JSで開閉アニメーションを実装する例

最後に、一応アニメーションをフルでJSに任せた実装例を紹介します。

Demo Preview
Open in new tab ↗
.accordions {
}

/* border を使うとjsでタイトルの高さ取得した時に少し数値がずれてバウンドすることに注意 */
.accordion {
	overflow: hidden;

	+ .accordion {
		margin-top: 1px;
	}
}

/* display:list-item 以外にしてデフォルトの三角アイコンを非表示にする */
.accordion__title {
	--bgc: #101010;
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 1em;
	background: var(--bgc);
	color: #fff;
	cursor: pointer;

	/* Safariで表示されるデフォルトの三角形アイコンを削除 */
	&::-webkit-details-marker {
		display: none;
	}

	/* アイコン擬似要素 */
	&::after {
		content: '';
		display: block;
		width: 1em;
		height: 1em;
		background-color: currentColor;

		/*  clip-path で > アイコンを描画し、回転させる  */
		clip-path: polygon(
			calc(35% + 4%) calc(10% - 4%),
			calc(75% + 4% * 2) 50%,
			calc(35% + 4%) calc(90% + 4%),
			calc(35% - 4%) calc(90% - 4%),
			calc(75% - 4% * 2) 50%,
			calc(35% - 4%) calc(10% + 4%)
		);
		rotate: 90deg;
		transition: rotate 0.3s;
	}
	.is-opened > &::after {
		rotate: -90deg;
	}
}
/* タイトルホバー時、背景色を少し半透明にする */
@media (any-hover: hover) {
	.accordion__title:hover {
		background-color: color-mix(in srgb, var(--bgc), transparent 15%);
	}
}
/* Tabキーでのフォーカス時も同じスタイリング */
.accordion__title:focus-visible {
	background-color: color-mix(in srgb, var(--bgc), transparent 15%);
}

.accordion__content {
	padding: 1.25em;
	background-color: #f2f2f2;
}

details/summary でのアニメーションに関しての基本的なことについては、ICSメディアさんの記事が非常に参考になりました。

それを元に、もう少しpaddingの融通が効くように調整を加えています。(コンテンツではなくdetailsheightをいじるようにした)

まとめ

本ページでは、detailssummary要素を使用したアコーディオンの実装方法を解説しました。

  • details/summary要素を使えば、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
details要素に命を吹き込む::details-content擬似要素の登場 details要素のコンテンツ部分にスタイルを適用できる::details-content擬似要素がBaseline 2025に加わりました。details要素を用いたスタイリングの守備範囲が広がっただけではなく、アニメーションの適用も容易になりました。
k8o
CSSのみでdetails要素のアニメーションを実装する方法 &#8211; TAKLOG
www.tak-dcxi.com
アクセシブルなアコーディオンの実装について考える
Zenn