「1つ開いたら、いま開いている項目を閉じる」。アコーディオンでよく書くこの処理は、もうJavaScriptが必須ではありません。
<details>へ同じname属性を付けると、同じグループの中で開ける項目を1つにできます。name属性は2024年9月からBaseline Newly availableで、Chrome・Edgeは120、Firefoxは130、Safariは17.2から使えます。
開閉そのものをブラウザへ任せたいFAQや補足情報なら、かなり小さなHTMLで作れます。
基本はdetailsとsummary
<details>は、内容を開いたり閉じたりできるHTML要素です。見出しになる部分には<summary>を入れます。
<details>
<summary>料金について</summary>
<p>料金の説明が入ります。</p>
</details>
クリックすると、ブラウザ標準の操作で内容が開閉します。ここまでは従来から使える書き方です。
同じnameを付けると、開けるのは1つだけになる
排他的なアコーディオンにするには、複数の<details>へ同じnameを付けます。
<div class="faq">
<details name="faq">
<summary>申し込み方法は?</summary>
<p>申し込み方法の説明。</p>
</details>
<details name="faq">
<summary>支払い方法は?</summary>
<p>支払い方法の説明。</p>
</details>
<details name="faq">
<summary>キャンセル方法は?</summary>
<p>キャンセル方法の説明。</p>
</details>
</div>
1つを開くと、同じname="faq"を持つ別の<details>は閉じます。排他制御のイベントリスナーは不要です。
複数の要素へ最初からopen属性を書いた場合は、ソース順で最初の1つだけが開いた状態になります。
nameは、置き場所では分かれない
ここで気をつけたいのが、nameの付け方です。同じページに別のFAQがある場合、そこへ同じnameを使うと、意図せず1つのグループとして動きます。<details>は隣り合っている必要がないので、<section>で囲んでも自動では分かれません。
ページ上部と下部に独立したFAQを置くなら、名前も分けます。
<details name="overview-faq">
<summary>概要について</summary>
<p>上部のグループの回答です。</p>
</details>
<details name="setup-faq">
<summary>設定について</summary>
<p>下部のグループの回答です。</p>
</details>
部品をCMSで複製するときは、表示した数だけでなくnameの値も確認したいところです。見た目は離れていても、グループの名前が同じなら連動します。
もう一つ、openは真偽属性です。属性があること自体が「開く」を意味するので、open="false"と書いても閉じた状態にはなりません。最初は閉じたいなら、open属性そのものを出力しないようにします。
同じグループの<details>を互いに入れ子にするのも避けます。ネストした開閉項目が必要なら、その項目のグループ名を別にするか、nameを付けない構成を考えます。
開いている項目の見た目はopen属性でも:openでも変えられる
<details>が開いているかどうかはopen属性で表されます。以前から使えるのは属性セレクタです。
details[open] > summary {
background: #eef4ff;
}
2026年5月からBaseline Newly availableになった:openも使えます。
details:open > summary {
background: #eef4ff;
}
古いブラウザまで対象にするなら、[open]のほうが対応期間は長くなります。:openは<dialog>や<select>などにも同じ考え方で使えるので、今後の新しいCSSをそろえて書きたい場合に選べます。
中身の装飾は普通の子要素でも十分できる
アコーディオンの内容を整えるだけなら、専用の疑似要素がなくても子要素へクラスを付けられます。
<details name="faq">
<summary>申し込み方法は?</summary>
<div class="faq__content">
<p>申し込み方法の説明。</p>
</div>
</details>
.faq__content {
padding: 1rem;
}
::details-contentという疑似要素も提案・実装が進んでいます。ただし、公式情報が食い違っています。Chromeの公式記事はChrome 131でのデモを掲載している一方、2026年9月16日時点のMDNの疑似要素ガイドには「まだどのブラウザも対応していない」という記述が残っています。
そのため、この記事では::details-contentをクロスブラウザ前提の実装には使いません。必要なら対象ブラウザを実機で確認したうえで、追加の装飾・アニメーションとして扱うのが安全です。
開閉をアニメーションさせるなら、別の対応状況も見る
name属性で「1つだけ開く」動作を作る部分はHTMLだけで完結します。一方、height: 0からheight: autoまで滑らかに動かすような演出は別です。
interpolate-sizeや::details-contentを組み合わせた例はChrome公式にありますが、これらの対応はdetails[name]ほど広がっていません。アニメーションを付ける場合は、未対応ブラウザで瞬時に開閉しても内容を読める設計を残します。
高さのアニメーションそのものはcorner-shapeとinterpolate-size、<dialog>やポップオーバーのフェードは@starting-styleとallow-discreteで、それぞれ対応状況と一緒に書いています。
また、動きを付けるならprefers-reduced-motionも一緒に考えます。
1つだけ開く形が、いつも読みやすいとは限らない
技術的にできることと、読みやすさは別の話です。回答を並べて比べたいFAQでは、開くたびに前の回答が閉じると不便かもしれません。説明を順番に読ませたい場合や、画面をコンパクトに保ちたい場合とは、向いている作りが違います。
nameを付けるか外すかで、この違いを選べます。JavaScriptを減らせることだけでなく、読む人が複数の項目を同時に見たいかどうかも、最初に考えておきたいところです。
JavaScriptを使うのは「開閉以外の仕事」があるとき
HTMLだけのアコーディオンで足りるのは、「見出しを押すと内容が開く」「同じグループでは1つだけ開く」という標準的な動きです。
開いた項目をURLへ保存する、解析イベントを送る、外部データを取得するなど、アプリ固有の処理が必要ならJavaScriptを追加します。
逆に、排他制御のためだけに状態管理を書いていたなら、details nameへ置き換えられる可能性があります。まずHTMLへ任せられる部分を任せると、コードも操作の意味も追いやすくなります!



