14
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【コピペでOK】JavaScriptを使わないアコーディオンメニュー

14
Posted at

コード (TailwindCSS v4)

See the Pen accordion by hrel11 (@hrel11) on CodePen.

コード (バニラCSS)

See the Pen accordion (vanillacss) by hrel11 (@hrel11) on CodePen.

HTML解説

本記事のアコーディオンメニューでは主にdetailsタグとsummaryタグを利用している。
たとえばChromium系ブラウザでは下図のようなDOMツリーが構成される。

image.png

2026年3月現在ブラウザによってShadow DOMの実装が微妙に異なる。
必ずしも上図と一致するわけではないことに注意

detailsタグ

HTML単体でアコーディオンメニューを構成できるタグ。

See the Pen Untitled by hrel11 (@hrel11) on CodePen.

  • open属性によりウィジェットの開閉状態を管理する
    • trueで開く
    • falseで閉じる(デフォルト値)
  • name属性により複数のdetailsタグを連動して開閉させることも可能
  • detailsタグ直下には2つのShadowDOMが自動的に描画される
    • summary部分の親要素(ウィジェットの開閉状態に関わらず常に表示される部分)
    • contents部分の親要素(開閉で表示/非表示になるコンテンツ部分)
  • summary部分の親要素ではTabキーによるフォーカスが効く。これによりEnterキーやSpaceキーで開閉操作をすることが可能

summaryタグ

ウィジェットの開閉状態に関わらず常に表示されるタグ。

See the Pen Untitled by hrel11 (@hrel11) on CodePen.

  • detailsタグから生成されるShadowDOMのうち、summary部分を上書きする

::details-content

「開閉で表示/非表示になるコンテンツ部分」を示す疑似要素。
detailsタグによって自動生成される。
image.png
::details-contentの子要素にはdetailsタグの中身(summaryタグを除く)が描画される

CSS解説

マーカーのリセットCSS

summary部分にはデフォルトで左側に三角マーカー(▼)が表示されるが、このマーカーを非表示にすることも可能

<summary class="list-none marker:content-none">
  見出し
  <svg />
</summary>

見た目をリッチにするにはマーカー部分の表現も重要となる。
そのためにはデフォルトの三角マーカーを無効化して代わりにimg要素やsvg要素でマーカーを描画する

See the Pen remove marker by hrel11 (@hrel11) on CodePen.

groupクラスによる状態連動

detailsタグの開閉状態(open属性)に連動して子要素のスタイルを定義できる。

<details class="group">
  <summary>
    <svg class="group-open:rotate-180">...</svg>
  </summary>
</details>

下記のコードでは開閉時にマーカーの向きが変化する。
マーカーにアニメーションを付けるならtransition-transform duration-[250ms]等を付与すればよい

See the Pen Untitled by hrel11 (@hrel11) on CodePen.

アニメーション設定

::details-contentの高さをgrid-template-rowsによって表現する。

details-content:grid
details-content:grid-rows-[0fr]      /* 閉じた状態 */
open:details-content:grid-rows-[1fr] /* 開いた状態 */

トランジションについてはこれらを併用する必要がある

/* 離散値プロパティのアニメーションを非表示にさせないために必要 */
details-content:transition-[content-visibility,grid-template]

/* アニメーション時間 */
details-content:duration-[250ms]

/* 離散値プロパティをアニメーションさせるために必要 */
details-content:transition-discrete
  • content-visibility をトランジション対象に含めることでアニメーション中に該当要素が非表示になってしまうことを防ぐ
  • transition-discrete (transition-behavior: allow-discrete;) は離散値を取るプロパティを滑らかにアニメーションさせるために必要となる
    • 今回の事例における「離散値を取るプロパティ」は grid-template-rows
    • 少なくともChromium系ブラウザではアニメーション実装のために本クラスが必須である(2026年3月現在)
  • これらによりgrid-template-rowsが1frから0frに変化する際にも滑らかにアニメーションすることができる

ブラウザの種別・バージョンによってはアコーディオンが閉じる際のアニメーションが効かない
(スマートフォンのSafariなど)

あらゆる環境で開閉アニメーションをさせたい場合にはJSでの実装を検討する

overflow-hiddenの役割

div要素に対してoverflow-hiddenを付与することで、::details-contentからはみ出すことがないようにする

<!-- 親要素で高さのアニメーションを実施 -->
::details-content

  <!-- 子要素はグリッドレイアウトに含まれる -->
  <div class="overflow-hidden">
  </div>

::details-contentの高さが変動しただけではアニメーションが可視化されない。

その原因は上記div要素がグリッドレイアウトの子要素となっていることにある。

グリッドレイアウトでは min-height: auto; の解釈が「コンテンツサイズに基づく最小の高さ」に変化することで、div要素は自身の高さを維持することを優先するようになる。

そのため::details-contentのアニメーション中においてもdiv要素がアニメーションを貫通して表示されてしまう。

参考記事

14
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?