アクセシブルなアコーディオン
見出し(h3)の中にボタンを置き、aria-expanded と aria-controls で開閉を伝える。↑↓ Home End で見出し間を移動。帯に「配送は…, 見出しレベル3, ボタン, 展開」を表示。
プレビュー
操作クリック/タップで動きます。
仕様書
- 向く場面
- FAQ・利用条件など見出しで折りたたむ本文
- 見出しジャンプで一覧できる構造にしたい場面
- 10 件を超える項目で↑↓移動が要る一覧
- 避ける場面
- div にクリックだけ付ける
- 矢印アイコンの向きだけで開閉を示す
- 開閉に関係なく全パネルを DOM から消す
- 構成要素
- 見出し(h3)
- 開閉ボタン
- 矢印アイコン
- パネル(region)
- 読み上げ文の帯
- 持っている状態
- 通常
- フォーカス
- キーボード
Tab で各見出しボタンへ、Enter/Space で開閉、↑↓で見出し間を移動、Home/End で端へ
- 支援技術
APG Accordion: h3 内の button に aria-expanded と aria-controls、パネルは role=region+aria-labelledby、閉じたパネルは hidden。矢印は aria-hidden
- 動き
矢印の回転 200ms。reduced-motion で停止
デザインの要点
- ボタンは見出しの中に入れる。見出しジャンプで一覧でき、押せることも伝わる。div にクリックを付けただけでは両方失う
- 開閉状態は aria-expanded で伝える。矢印アイコンの向きは読み上げには存在しない
- パネルに role=region と aria-labelledby を付けると、ランドマーク一覧から中身へ直接飛べる
- ↑↓ での見出し移動は任意だが、10 件を超える FAQ では Tab の回数が倍になるので入れる価値がある
使いどころ
FAQ、利用条件、設定の詳細など、見出しで区切れる本文を折りたたむときに使う。全部を読む人と目的の1件だけ読む人が混在する内容に向く。逆に、順番に読み進めることが前提の文章を折りたたむと、全体像が掴めないまま断片を開く操作だけが増える。項目が3件以下で本文も短いなら、折りたたまずに並べた方が速い。中身がフォームの複数ステップなら、アコーディオンよりステッパーの方が進捗と順序を伝えられる。なお、検索エンジンは折りたたまれた中身も読むので、SEO を理由に開閉を決める必要はない——判断基準は読み手の使い方に置く。
仕組み
各項目の見出しを h3 にし、その中に button を置く。button に aria-expanded で開閉を、aria-controls でパネルの id を持たせ、クリックで aria-expanded を反転させてパネルの hidden 属性を切り替える。パネル側は role="region" と aria-labelledby で見出しボタンを指し、ランドマーク一覧から中身へ直接飛べるようにしている。↑↓ Home End の keydown で見出しボタン間を移動する操作も足してあるが、こちらは APG でも任意扱い。矢印アイコンは aria-hidden="true" で読み上げから外し、状態は aria-expanded だけが伝える。見本は focus と click の両方で読み上げ文を組み立てて帯に出すので、「配送は…, 見出しレベル3, ボタン, 展開」のうち、どこが構造(h3)由来でどこが属性由来かが分かる。
実装の注意
div に onclick を付けただけの実装が最も多く、キーボードで押せない・読み上げでボタンと分からない・見出しジャンプの一覧に出ない、の3つを同時に失う。button を h3 の外へ置くのも惜しい形で、押せることは伝わるが見出し構造から外れる。次に多いのが、開閉を矢印アイコンの回転だけで示す実装——CSS の transform は読み上げに存在しないので、aria-expanded が無ければ状態は伝わらない。パネルを height:0 と overflow:hidden で隠す場合、中のリンクは Tab 順に残るため、閉じたパネルへフォーカスが飛ぶ。高さをアニメーションさせたいなら inert を併用するか、トランジション終了後に hidden を付ける。button の中にリンクを入れ子にするのも不正な構造で、キーボード操作が壊れる。
アクセシビリティ
WAI-ARIA Authoring Practices の Accordion パターンに沿う。見出しレベルは周囲の文書構造に合わせる(見本はカードの中なので h3)。読み上げ利用者の多くは見出しジャンプで目次のように移動するため、ボタンを見出しの中へ置くことが最大の効用になる。aria-expanded は true/false のどちらかを常に持たせる——属性ごと消すと「開閉する要素ではない」と解釈される。パネルの role="region" は項目数が少ないときに有効だが、10 件を超えるとランドマーク一覧が region で埋まるので、その場合は付けないという判断もある(APG も必須とはしていない)。閉じたパネルは hidden 属性で、読み上げとフォーカス順の両方から同時に外す。
調整して使う
まず同時に開ける数。複数同時に開ける(見本の既定)か、1つ開くと他が閉じる排他型かを選ぶ。排他型は見比べづらくなるので、FAQ では複数開ける方が使いやすい。次に初期状態で、全閉じが基本だが、最もよく読まれる1件だけ開いておくと「開ける操作ができる」こと自体が伝わる。3つめは矢印キーの扱いで、見本は ↑↓ で見出し間を移動するが、項目が5件以下なら Tab だけで足りるので省いてよい。アイコンは向き(∨→∧)でも +/− でも伝わるが、どちらも aria-hidden にして状態は aria-expanded に任せる。見出しの行全体を押せる範囲にすると、タップ標的 44px を確保しやすい。
コード(コピーして使えます)
<div class="acx-wrap" id="acx-wrap">
<h3 class="acx-h"><button type="button" class="acx-btn" id="acx-b1" aria-expanded="true" aria-controls="acx-p1"><span>配送はいつ届きますか</span><svg class="acx-ico" aria-hidden="true" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></button></h3>
<div class="acx-panel" id="acx-p1" role="region" aria-labelledby="acx-b1"><p>ご注文から 2 営業日以内に発送し、翌日〜翌々日にお届けします。時間帯指定は注文画面で選べます。</p></div>
<h3 class="acx-h"><button type="button" class="acx-btn" id="acx-b2" aria-expanded="false" aria-controls="acx-p2"><span>返品はできますか</span><svg class="acx-ico" aria-hidden="true" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></button></h3>
<div class="acx-panel" id="acx-p2" role="region" aria-labelledby="acx-b2" hidden><p>未使用・到着後 14 日以内なら可能です。返送料は当店負担で、返金は 5 営業日以内に元の支払い方法へ戻します。</p></div>
<h3 class="acx-h"><button type="button" class="acx-btn" id="acx-b3" aria-expanded="false" aria-controls="acx-p3"><span>領収書は発行できますか</span><svg class="acx-ico" aria-hidden="true" width="16" height="16" viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></button></h3>
<div class="acx-panel" id="acx-p3" role="region" aria-labelledby="acx-b3" hidden><p>注文履歴から宛名を入れて PDF を発行できます。適格請求書(インボイス)の登録番号も記載されます。</p></div>
<div class="acx-band">
<span class="acx-tag">読み上げ</span><span id="acx-sr">配送はいつ届きますか, 見出しレベル3, ボタン, 展開</span>
<span class="acx-keys" aria-hidden="true"><kbd>↑</kbd><kbd>↓</kbd><kbd>Home</kbd><kbd>End</kbd></span>
</div>
</div>