開閉・展開
ディスクロージャー(開閉)
解説ありDisclosure
「もっと見る」のように1つの領域を表示/非表示するもっともシンプルな開閉パターン。
ディスクロージャー(開閉)とは?
ディスクロージャーは、1つのボタンを押すと、その下の1つの領域が 開いたり閉じたりする、もっともシンプルな開閉 UI です。 「もっと見る」「詳細を表示」「FAQ の答えを開く」などでよく使われます。
アコーディオンが「複数の見出し+複数のパネル」をまとめたものなのに対し、 ディスクロージャーはボタン1つ・領域1つの最小単位だと考えると分かりやすいです。
なぜアクセシビリティが大事なの?
見た目だけ作ると、次の人たちが取り残されます。
- キーボードだけで操作する人。トリガーが
<div>だとTab でフォーカスできず、そもそも開けません。 - スクリーンリーダーを使う人。ただの
<div>は「押せるもの」と認識されず、 いま開いているのか閉じているのかも伝わりません。
解決策はシンプルです。トリガーを <button> にして、aria-expanded で開閉状態を伝えるだけです。
ライブデモ(推奨実装)
下の「もっと見る」ボタンは APG に沿った実装です。マウスを使わず、キーボードだけで操作してみてください。
アクセシビリティとは、年齢や障害の有無にかかわらず、 誰もが情報やサービスを利用できるようにする考え方です。
Web では WCAG(Web Content Accessibility Guidelines)が国際的な基準として知られています。 キーボード操作・色のコントラスト・スクリーンリーダー対応などが含まれ、 多くの国で公的サイトの要件にもなっています。
試してみよう:Tab でボタンに移動 → Enter / Space で開閉。ラベルが「もっと見る」⇄「閉じる」に変わります。
ポイント
スクリーンリーダー(macOSなら ⌘+F5 で VoiceOver)をオンにすると、 ボタンにフォーカスしたとき「もっと見る, 折りたたみ, 展開済み」のように役割と状態が読み上げられます。これが aria-expanded の効果です。
キーボード操作
| キー | 動作 | 必須/任意 |
|---|---|---|
| Enter / Space | 領域を開く / 閉じる | 必須 |
| Tab | 次 / 前のフォーカス可能要素へ移動 | 必須 |
補足
Enter と Space での開閉は、トリガーを <button> にするだけで自動的に得られます。自分でキー処理を書く必要はありません。これがネイティブ要素の強みです。
必要な WAI-ARIA / ロール
| 付ける場所 | 属性 / ロール | 意味 |
|---|---|---|
| トリガー | <button type="button"> | 押せる要素にする。フォーカス・Enter/Space・「ボタン」ロールが自動で付く。 |
| トリガー | aria-expanded="true | false" | 領域が開いているか。状態の変化に合わせて必ず更新する。 |
| トリガー | aria-controls="領域のid" | どの領域を制御するボタンかを示す。 |
| 閉じている領域 | hidden | 閉じている間はDOMから隠し、キーボード/読み上げの対象外にする。 |
実装:推奨パターン(Good)
良い例 / 推奨
トリガーを <button> にし、aria-expanded を状態と同期させます。
マークアップ:
<button type="button"
id="more-btn"
aria-expanded="false"
aria-controls="more-region">
もっと見る
</button>
<div id="more-region" hidden>
<p>ここに隠していた詳しい説明が入ります。</p>
</div>開閉のスクリプト(状態の同期がすべて):
const trigger = document.getElementById('more-btn');
const region = document.getElementById('more-region');
if (trigger && region) {
trigger.addEventListener('click', () => {
const expanded = trigger.getAttribute('aria-expanded') === 'true';
// 状態を反転して aria-expanded と表示・ラベルを同期させる
trigger.setAttribute('aria-expanded', String(!expanded));
region.hidden = expanded;
trigger.textContent = expanded ? 'もっと見る' : '閉じる';
});
}補足
ボタンのラベルを「もっと見る」⇄「閉じる」のように切り替えると、 晴眼のユーザーにも次の動作が伝わって親切です。aria-expanded の更新は必ず行ってください(見た目だけ変えても支援技術には伝わりません)。
アンチパターン(Bad)
下は <div> と onclick だけで作った「見た目だけ同じ」開閉です。マウスでは開けますが、キーボードでは一切操作できません。上のデモと同じように Tab → Enter を試して、違いを体感してください。
アクセシビリティとは、年齢や障害の有無にかかわらず、 誰もが情報やサービスを利用できるようにする考え方です。
Web では WCAG が国際的な基準として知られています。 キーボード操作・色のコントラスト・スクリーンリーダー対応などが含まれます。
試してみよう:Tab を押しても「もっと見る」にフォーカスが当たりません。スクリーンリーダーでは「ボタン」とも認識されず、開閉できることすら伝わりません。
<!-- ❌ アンチパターン -->
<!-- div なのでキーボードでフォーカスできず、ボタンとも認識されない -->
<div class="more" onclick="toggle()">もっと見る +</div>
<div id="more" style="display:none">
<p>ここに隠していた詳しい説明が入ります。</p>
</div>悪い例 / 避ける
この実装の問題点:
- キーボードで操作できない —
divはフォーカスを受け取れない。 - 役割が伝わらない — スクリーンリーダーには「ただのテキスト」に聞こえ、押せると分からない。
- 状態が伝わらない —
aria-expandedがなく、開/閉が判別できない。 display:noneのインラインstyle頼みで、hidden属性や状態管理が無い。
ポイント
どうしても <div> を使うなら、role="button"・tabindex="0"・Enter/Space のキー処理・aria-expanded をすべて自前で足す必要があります。 最初から <button> を使えば、その大半がタダで手に入ります。
実装チェックリスト
- トリガーは
<button type="button">である aria-expandedが開閉と常に一致しているaria-controlsで領域のidを指している- 閉じている領域は
hiddenでキーボード/読み上げから外れている - キーボードだけで開閉でき、フォーカスが見える(フォーカスリングを消していない)
- (任意)ボタンのラベルが開閉に合わせて切り替わる