1つ開くとほかが閉じるアコーディオンが、HTMLだけで作れる

「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へ任せられる部分を任せると、コードも操作の意味も追いやすくなります!

SNS SHARE

はてなブックマークでシェアXでシェアLINEでシェア