アコーディオンUIは、限られたスペースに多くの情報を整理して表示できる便利なコンポーネントです。FAQページやサイドメニュー、設定画面など、さまざまな場面で活用されています。
昔はアコーディオンの開閉機能を実装するにはJavaScriptが必須でしたが、HTML5で導入されたdetailsとsummary要素を使えば、JavaScriptなしでアクセシブルなアコーディオンを実装できます。
このページでは、detailsとsummaryを用いたアコーディオンの構築方法を解説します。
details/summary要素についての基礎知識
details/summary要素には開閉機能がネイティブで備わっており、JavaScriptが必要ありません。
また、CSSでスタイリングしなくてもデフォルトで左側に三角形のマーカー(▶)が表示され、展開時には方向が変わる(▼)ようになっており、HTMLだけでも最低限の機能と見た目が担保されています。
まずはシンプルな例を見てみましょう。
<details> <summary>クリックして詳細を表示できます</summary> <p>ここに詳細な内容が入ります。</p></details>summary要素は、details要素の最初の子要素として配置する必要があります。
このHTMLを書くだけで、以下のようになります。
クリックして詳細を表示できます
ここに詳細な内容が入ります。
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で実装するアコーディオン
それでは、実際にdetailsとsummaryを使ってアコーディオンを実装してみましょう。
/* アコーディオン全体のコンテナスタイル */
.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点。
height: autoではアニメーションができない。open属性が外れている時、コンテンツがレンダリングから完全に除外されてしまう。(display:noneからフェードインできないのと同じ)
しかしながら、最新のCSSでは、CSSだけでもアニメーションが可能になりつつあります。
- 1を解決するTips :
grid-template-rowsを0fr~1frへトランジションさせるというテクニックがあります。また、将来的にはinterpolate-size: allow-keywords;を使うことでheight:autoのアニメーションが可能になります。 - 2を解決するTips :
::details-content擬似要素を使うことで、コンテンツ用のslot要素に対してスタイルを付与することができるようになり、アニメーションも可能になりました。
interpolate-size, ::details-contentはまだブラウザ対応が完全ではないため積極的に導入はしずらいですが、将来的にはこれらを使ってCSSだけで開閉アニメーションを実装できるようになります。
ブラウザサポート状況(2025年12月時点)
::details-contentは主要ブラウザで広くサポートされはじめていますが、interpolate-sizeはChromium系のみの対応となっています。非対応ブラウザではアニメーションなしで即座に開閉してしまいます。
CSSだけで開閉アニメーションを実装する例
::details-content に対して 0fr~1frのトランジションTispを使い、CSSだけで開閉アニメーションを実装する例を紹介します。
(interpolate-size: allow-keywords;を使ってheightをアニメーションさせた方が動きが滑らかな気がしたので、将来的にはそちらを使いたいですが、ひとまずサポート状況を優先しています。)
/* ----- 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-rowsを0fr~1frへトランジションさせる.transition-property: content-visibility&transition-behavior: allow-discreteの指定を忘れないように.- また、その子要素に対して
overflow: hiddenが必要なので、コンテンツはdiv(.accordion__content)でラップする. ::details-contentを使うのは、まだあくまでアニメーション部分だけ(非サポートブラウザではただアニメーションがなくなるだけですむように。)- コンテンツに
paddingをつける時は.accordion__contentにつけて、open属性のない時はpadding-blockを0するようにする。もしくは、さらに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属性をつけ外しすることです。
.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に任せた実装例を紹介します。
.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の融通が効くように調整を加えています。(コンテンツではなくdetailsのheightをいじるようにした)
まとめ
本ページでは、detailsとsummary要素を使用したアコーディオンの実装方法を解説しました。
details/summary要素を使えば、JavaScriptなしでアクセシビリティの高いアコーディオンが実装できるだけでなく、開閉時のスライドアニメーションについても、CSSだけで実装できるようになっています。
参考リンク