見出し(ヘッダー)をクリックすると対応するコンテンツパネルを展開・折りたたむUIコンポーネント。FAQ・設定のカテゴリ分け・長いページの段階的開示などで使われ、「すべての情報を同時に表示しなくてもよい」状況でページの縦スクロールを大幅に削減する。
この記事を読むと、単一展開(同時に1つだけ)と複数展開(複数同時可)の使い分け・ARIA disclosure pattern(aria-expanded + aria-controls)・デフォルトで開いておく項目の設計・TabsとAccordionの使い分けが自分でできるようになります。
1. UI例(Preview / Live)
実装で見る(GunjoUI)
この部品を、デザインシステム GUNJO の実装で確かめられます。
2. 定義(Definition)
見出し(トリガーボタン)をクリックすると対応するコンテンツパネルを展開・折りたたむUIコンポーネント。WAI-ARIA では「Disclosure Pattern」または「Accordion Pattern」として定義されており、トリガーボタンに aria-expanded と aria-controls を付与してパネルと意味論的に紐付ける。
TabsとAccordionの違い:Tabsは横並びで同時に1つのパネルのみ表示し、常にどれかが選択された状態。Accordionは縦積みで複数同時展開が可能(モードによる)、すべて折りたたんだ状態も許容される。モバイルではTabsの代わりにAccordionを使うレスポンシブパターンも一般的。
単一展開 vs 複数展開:単一展開は1つを開くと他が閉じる(Radioに近い動作)。複数展開は独立して開閉できる(Checkboxに近い動作)。どちらが適切かはコンテンツの性質に依る——比較参照が不要なら単一展開、複数を同時参照したいなら複数展開。
3. 使い分け(When to use / When NOT to use)
3.1 When to use
- FAQ(よくある質問):質問の見出し一覧を先にスキャンさせ、必要な答えだけ開く
- 設定画面のカテゴリ分け:「通知設定」「プライバシー」「アカウント」などを折りたたんでページを整理する
- 長いフォームのセクション分け:入力済みのセクションを折りたたんで進捗を管理する
- モバイルでのTabs代替:横幅が足りないためTabsが使えない場合
3.2 When NOT to use
- すべてのユーザーが必ず読むコンテンツを折りたたむ → 重要情報が隠れてユーザーが見逃す
- コンテンツが少量(2〜3項目・テキストが短い)→ 常時展開表示の方がシンプル
- ステップごとの進行が必要な場合 → Stepper(ウィザード)を使う
- コンテンツを横並びで比較したい →
Tabsを使う
3.3 代替UI(Alternatives)
- 横並びコンテンツ切り替え →
Tabs - 詳細情報のみ折りたたむ →
<details>+<summary>(HTMLネイティブ) - 段階的なステップ → Stepper / Wizard
- 補足情報のオーバーレイ →
Popover/Tooltip
4. 設計判断の核(Decision Principles)
Accordionの核は「何を隠すか」の判断——ユーザーの半数以上が読まないコンテンツだけを折りたたむ。重要なコンテンツを折りたたむと発見率が下がり、ユーザーが行動を完了できなくなる。
判断の優先順位:① 隠すコンテンツの適切性 → ② 単一/複数展開の選択 → ③ デフォルト展開項目 → ④ ARIAの正確な実装
- 単一展開は「セクションが独立している・比較不要」の場合:設定の各カテゴリのように、同時に参照する必要がない場合は単一展開で画面をすっきりさせる。ユーザーが「なぜ閉じるの?」と感じる場合は複数展開に切り替える
- 複数展開は「ユーザーが自分で管理する」FAQに適する:FAQでは複数の質問を同時に参照したいケースが多い。単一展開にすると「さっきの答えをもう一度確認したい」時に再展開が必要になり手間が増える
- デフォルトで開く項目は「最もよく参照される1〜2項目」に限る:すべて閉じた状態だとコンテンツの存在に気づかれにくい。最初の項目または最重要の項目をデフォルトで開いておく
- トリガーは
<button>を<h2>/<h3>で囲む:見出しレベルのHTMLで構造化することで、スクリーンリーダーのユーザーが見出しナビゲーション(Hキー)でアコーディオン項目にジャンプできる
5. 状態設計(States)
5.1 必須状態(Required)
- Collapsed(折りたたみ):
aria-expanded="false"、パネル非表示 - Expanded(展開中):
aria-expanded="true"、パネル表示、矢印アイコン反転
5.2 条件付き状態(Conditional)
- Hover / Focus:ヘッダーボタンのハイライト・フォーカスリング
- Disabled(無効):
disabled属性、操作不能
5.3 State Gallery
| 状態 | 必須 | 何を伝えるか |
|---|---|---|
| Collapsed | ✅ | このパネルは折りたたまれている(aria-expanded="false") |
| Expanded | ✅ | このパネルは展開されている(aria-expanded="true") |
| Hover / Focus | ✅ | クリック可能・現在フォーカス中 |
| Disabled | — | この項目は現在展開できない |
6. バリエーション設計(Variants)
単一/複数展開とスタイルの組み合わせ。
| バリアント | 展開モード | 典型的な用途 |
|---|---|---|
| 単一展開(Exclusive) | 1つだけ展開 | 設定カテゴリ・ナビゲーション |
| 複数展開(Independent) | 複数同時展開可 | FAQ・コンテンツ一覧 |
| デフォルト全閉 | — | スキャン優先のFAQ |
| デフォルト1件展開 | — | 最重要コンテンツを初期表示 |
禁止パターン:重要なCTA(ボタン)や必須入力フィールドをAccordionの中に隠す → ユーザーが展開しないと行動を完了できなくなる。
7. パターン集(Good / Bad / How to fix)
7.0 よく崩れる設計パターン(3つ)
aria-expandedがなく展開状態が分からない:スクリーンリーダーが「ボタン」としか読まず、展開・折りたたみ状態を伝えられない- 見出しを
<div>でマークアップ:見出しナビゲーション(Hキー)でアコーディオン項目にジャンプできず、スクリーンリーダーユーザーのナビゲーション効率が大きく落ちる - 重要なコンテンツを折りたたむ:「はじめに」や「必読」に相当する情報をデフォルトで折りたたみ、多くのユーザーが読まずにページを離れる
7.1 Bad(典型3つ)
<div onClick={toggle}>よくある質問1</div>のように<div>をトリガーにしており、キーボード操作もARIAも未実装<button>にaria-expandedがなく、スクリーンリーダーが展開状態を「ボタン。よくある質問1」としか読み上げない- 「利用規約に同意する」チェックボックスがAccordionの中に隠れており、展開しないとフォーム送信ができない
7.2 Good(対になる3つ)
<h3><button aria-expanded={isOpen} aria-controls={panel-${id}}>...</button></h3>で見出し + ボタンの正しい構造にするaria-expanded={isOpen}をトリガーボタンに付与し、スクリーンリーダーが「折りたたまれています。ボタン」または「展開されています。ボタン」と読み上げる- 必須の操作・重要なCTAはAccordionの外に配置し、Accordionには補足情報や詳細コンテンツのみを格納する
7.3 How to fix(手順)
<div>のトリガーを<button>に変更し、<h2>または<h3>で囲む- トリガーボタンに
aria-expanded={isOpen}とaria-controls="panel-id"を追加する - パネルに
id="panel-id"・role="region"・aria-labelledby="trigger-id"を追加する - パネルの表示/非表示は
hidden属性または CSS のmax-heightアニメーションで制御する
8. ルール(Must / Better)
Must(守らないと壊れる)
Better(品質が跳ねる)
9. アクセシビリティ要件(必須)
Keyboard
Tabでアコーディオンのトリガーボタン間をフォーカス移動Enter/Spaceでトリガーを操作して展開/折りたたむ- パネルが展開された状態で
Tabを押すと、パネル内のインタラクティブ要素(リンク・ボタン)にフォーカスが移る
Focus
- フォーカスリングをトリガーボタンに表示する
Tabの移動順はトリガーボタン → パネル内のフォーカス可能要素の順
Screen Reader
<div>
<h3>
<button
id="trigger-1"
aria-expanded="true"
aria-controls="panel-1"
>
Accordionはいつ使うべきですか?
<!-- 矢印アイコン(aria-hidden="true") -->
</button>
</h3>
<div
id="panel-1"
role="region"
aria-labelledby="trigger-1"
>
<p>コンテンツが多く...</p>
</div>
</div>
矢印アイコン(SVG)は aria-hidden="true" を付与してスクリーンリーダーに読まれないようにする。
Touch / Pointer
- トリガーボタンの高さは最低44px
- タップエリアをボタン全体(見出しテキスト + 矢印アイコンを含む行全体)に広げる
Contrast / Readability
- 展開/折りたたみの矢印アイコンと背景のコントラスト比は3:1以上(UIコンポーネント要素)
- 本文テキストと背景のコントラスト比は4.5:1以上
10. 実装メモ(Implementation Notes)
- shadcn/ui の
Accordionは@radix-ui/react-accordionベースで、type="single"/type="multiple"の切り替え・aria-expanded+aria-controls・role="region"+aria-labelledby・キーボードナビゲーションがすべて実装済み max-heightアニメーションはコンテンツの高さが動的に変わる場合に使う。max-height: 0 → max-height: 1000pxのトランジションでコンテンツが可変長でも展開アニメーションが機能する。ただしmax-heightの値が大きすぎるとアニメーション速度が一定にならないため、@radix-ui/react-accordionのdata-state+ CSS keyframes が最も正確hidden属性(display: none)でパネルを非表示にするとスクリーンリーダーに読まれなくなる。アニメーションが不要ならhidden属性が最もシンプルな実装- HTMLネイティブの
<details>+<summary>要素は単純なDisclosureには有効だが、アニメーション・スタイリング・グループ管理(単一展開)の実装が複雑になるため、アプリケーションには shadcn/ui を使う方が現実的
11. 関連リンク
- 関連するUIデザイン原則: 段階的開示 (Progressive Disclosure), 情報密度の最適化 (Information Density), スキャンしやすさ (Scannability)
- 用語集(定義): 認知負荷 (Cognitive Load)
- 関連するUIコンポーネント(横): Tabs(タブ), Separator(セパレーター)
12. まとめ
Accordionの設計で最重要なのは「何を隠すか」の判断と、aria-expanded + aria-controls の正確な実装です。迷ったら 4. 設計判断の核 に戻り、「重要なコンテンツを隠していないか」「トリガーを <h2>/<h3> で囲んでいるか」「aria-expanded は付いているか」の3点を確認してください。shadcn/ui の Accordion を使えばARIAパターンとアニメーションはすべて解決されます。TabsとAccordionの選択に迷ったら——「スペースが限られる・項目数が多い・モバイル」ならAccordion、「横並び・デスクトップ・常に見えていてほしい」ならTabsというシンプルな判断基準で選んでください。