実践!アクセシブルなアコーディオンコンポーネントの実装
はじめに
Webサイトやアプリケーションにおいて、コンテンツを整理し、省スペースで表示するためにアコーディオンコンポーネントは広く利用されています。しかし、適切に実装されていないアコーディオンは、特定のユーザーグループにとって操作が困難になることがあります。特に、キーボードのみで操作するユーザーや、スクリーンリーダーを利用するユーザーは、アコーディオンの状態変化(開閉)を認識できなかったり、操作自体ができなかったりといった課題に直面する可能性があります。
本記事では、ウェブアクセシビリティの観点から、アクセシブルなアコーディオンコンポーネントを実装するための具体的な手順とポイントを、コード例を交えながら解説します。
なぜアコーディオンのアクセシビリティが必要なのか
アコーディオンコンポーネントにおけるアクセシビリティの課題は主に以下の点に集約されます。
- キーボード操作性: マウスを使用しないユーザーが、アコーディオンの各項目にフォーカスを移動させ、開閉操作をキーボードで行える必要があります。標準的なHTML要素(例:
button
)を使用しない場合、追加のJavaScriptによるキーボードイベントハンドリングが必要になります。 - 状態の伝達: アコーディオンの項目が「開いている」のか「閉じている」のか、現在の状態を視覚情報に依存しない形でユーザーに伝える必要があります。特にスクリーンリーダーユーザーにとって、この状態情報はコンテンツを理解する上で不可欠です。
- コンテンツへのアクセス: アコーディオンが閉じている状態でも、その中に含まれるコンテンツの概要を理解できることや、必要に応じてコンテンツへスムーズにアクセスできることが望ましいです。
これらの課題に対応することで、キーボードユーザー、スクリーンリーダーユーザー、認知障害のあるユーザーなど、より多くの人々がアコーディオン内のコンテンツへ問題なくアクセスできるようになります。
具体的な実装手順
ここでは、ボタン要素とWAI-ARIA属性を使用して、一般的なアコーディオンコンポーネントをアクセシブルに実装する手順を解説します。
1. 基本的なHTML構造
アコーディオンの各項目は、見出しとなる要素(通常はボタン)とその内容パネルで構成されます。セマンティクスを考慮し、ボタン要素をトリガーにすることが推奨されます。
<div class="accordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button" type="button" aria-expanded="false" aria-controls="section1">
セクション1のタイトル
</button>
</h2>
<div id="section1" class="accordion-content" role="region" aria-labelledby="section1-header" hidden>
<!-- セクション1の内容 -->
<p>ここにセクション1の詳細な内容が入ります。</p>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button" type="button" aria-expanded="false" aria-controls="section2">
セクション2のタイトル
</button>
</h2>
<div id="section2" class="accordion-content" role="region" aria-labelledby="section2-header" hidden>
<!-- セクション2の内容 -->
<p>ここにセクション2の詳細な内容が入ります。</p>
</div>
</div>
<!-- 他のaccordion-itemも同様に記述 -->
</div>
button
要素: アコーディオンのタイトル部分にはbutton
要素を使用します。これにより、キーボードでの操作(Tabキーでのフォーカス移動、Enter/Spaceキーでの実行)が可能になります。見出し要素(h2
など)の中に配置することで、コンテンツの構造も明確になります。ボタンに適切なtype="button"
を指定します。aria-expanded
: ボタンにaria-expanded
属性を付与します。この属性は、折りたたみ可能な要素(ここでは内容パネル)の状態を示します。パネルが閉じているときはfalse
、開いているときはtrue
に設定します。スクリーンリーダーはこれを読み上げ、ユーザーに現在の状態を伝えます。aria-controls
: ボタンにaria-controls
属性を付与し、そのボタンが制御する内容パネルのid
を指定します。これにより、ボタンとパネルの関連付けが明確になります。- 内容パネル (
div.accordion-content
): 内容パネルには、スクリーンリーダーがその領域をランドマークや特定の役割として認識できるようにrole="region"
を付与することが推奨される場合があります(必須ではありませんが、大規模なコンテンツを含む場合に有用です)。また、aria-labelledby
を使用して、対応するボタンのid
を参照することで、パネルの内容がどの見出しに関連しているかを明確にできます。初期状態ではhidden
属性を付与するか、CSSでdisplay: none;
を設定して非表示にします。
2. CSSによるスタイリング
アコーディオンの見た目を整え、開閉状態を視覚的に表現します。また、キーボードフォーカスインジケーターを明確に表示することも重要です。
/* 基本スタイル */
.accordion-button {
/* buttonのデフォルトスタイルをリセットする場合 */
background: none;
border: none;
padding: 10px;
width: 100%; /* 親要素の幅に合わせる */
text-align: left;
cursor: pointer;
font-size: 1em;
/* その他のスタイル(色、マージンなど) */
}
/* フォーカスインジケーター */
.accordion-button:focus {
outline: 2px solid blue; /* または好みのスタイル */
outline-offset: 2px;
}
/* 開閉状態によるスタイル */
.accordion-button[aria-expanded="true"] {
font-weight: bold; /* 開いている状態のタイトルを太字にするなど */
}
.accordion-content {
padding: 10px;
border-top: 1px solid #ccc; /* パネルの区切り線 */
}
/* 初期状態または閉じている状態 */
.accordion-content[hidden] {
display: none;
}
/* 開いている状態 */
.accordion-button[aria-expanded="true"] + .accordion-content {
display: block;
}
aria-expanded
属性セレクター ([aria-expanded="true"]
) を使用して、ボタンや内容パネルのスタイルを開閉状態に応じて変化させます。:focus
スタイルは、キーボードユーザーが現在どの要素にフォーカスしているかを明確に把握するために不可欠です。ブラウザのデフォルトスタイルを利用するか、独自に定義します。
3. JavaScriptによる開閉機能の実装
ボタンクリック時やキーボード操作時に、内容パネルの表示/非表示を切り替え、aria-expanded
属性の値を更新します。
document.addEventListener('DOMContentLoaded', () => {
const buttons = document.querySelectorAll('.accordion-button');
buttons.forEach(button => {
const content = document.getElementById(button.getAttribute('aria-controls'));
if (!content) {
console.error(`対応するコンテンツが見つかりません: ${button.getAttribute('aria-controls')}`);
return;
}
// 初期状態の設定(HTMLでhidden属性を付与している場合は不要な場合も)
// button.setAttribute('aria-expanded', 'false');
// content.setAttribute('hidden', ''); // hidden属性または style.display = 'none'
button.addEventListener('click', () => {
const isExpanded = button.getAttribute('aria-expanded') === 'true';
button.setAttribute('aria-expanded', String(!isExpanded));
content.toggleAttribute('hidden', isExpanded);
// (任意) 他のアコーディオン項目を閉じる場合
// closeOtherAccordionItems(button);
});
// キーボード操作 (Enter/Space は button 要素のデフォルト動作で実行されるため、明示的なハンドリングは不要)
// 必要に応じて、矢印キーでのフォーカス移動などを実装することも可能ですが、
// シンプルなアコーディオンであれば Tab キー移動で十分な場合が多いです。
});
// (任意) 他のアコーディオン項目を閉じる関数
function closeOtherAccordionItems(clickedButton) {
buttons.forEach(button => {
if (button !== clickedButton && button.getAttribute('aria-expanded') === 'true') {
const contentToClose = document.getElementById(button.getAttribute('aria-controls'));
button.setAttribute('aria-expanded', 'false');
contentToClose.setAttribute('hidden', '');
}
});
}
});
aria-expanded
属性の値を真偽値に応じて切り替えます。属性値は文字列 ("true"
または"false"
) であることに注意してください。- 内容パネルの表示/非表示は、
hidden
属性の付け外し、またはCSSのdisplay
プロパティの切り替えで行います。hidden
属性を使用すると、要素が不可視であることのセマンティクスがより明確になります。 button
要素を使用している場合、EnterキーやSpaceキーによるクリックイベントはブラウザのデフォルト動作で発生するため、個別のキーボードイベントリスナーを追加する必要はありません。ただし、上下矢印キーなどでアコーディオン間を移動させたい場合は、追加のハンドリングが必要です。- アコーディオンの動作として「一つが開くと他が閉じる」という仕様にする場合は、クリックされたボタン以外の項目を特定し、それらを閉じる処理を追加します(上記の
closeOtherAccordionItems
関数の例を参照)。この場合、複数のパネルが同時に開かないことを保証する必要があります。
実装時の注意点
- セマンティクス: アコーディオンのタイトルには
button
要素を使用し、見出し要素(h2
など)の中に配置することが推奨されます。これにより、ドキュメントのアウトラインが適切に保たれ、スクリーンリーダーユーザーが見出しリストでコンテンツ構造を把握しやすくなります。 - ARIA属性の適切な使用:
aria-expanded
とaria-controls
はセットで使用し、正確なid
を指定してください。不要なARIA属性の追加は、かえって混乱を招く可能性があります。 - キーボードフォーカス:
button
要素を使用すれば、デフォルトでTabキーによるフォーカス移動が可能になります。フォーカスされた要素が視覚的に明確になるよう、:focus
スタイルを必ず設定してください。 - 表示/非表示の切り替え: 内容パネルの表示/非表示は、
display: none;
またはhidden
属性で行ってください。visibility: hidden;
やopacity: 0;
だけでは、要素がレンダーツリーから完全に削除されず、スクリーンリーダーが内容を読み上げてしまう可能性があります。display: none;
またはhidden
属性は、要素をアクセシビリティツリーからも除外します。
テスト方法
実装したアコーディオンがアクセシブルであるかを確認するために、以下のテストを行います。
- キーボード操作:
- Tabキーを使って、ページ内のアコーディオンの各タイトル(ボタン)にフォーカスが移動することを確認します。
- フォーカスが当たっている状態でEnterキーまたはSpaceキーを押し、内容パネルが開閉することを確認します。
- アコーディオンが開閉する際に、意図しないフォーカス移動が発生しないか確認します(コンテンツの先頭などに自動的に移動させたい場合は別ですが、多くの場合、トリガーボタンにフォーカスが維持されるのが自然です)。
- スクリーンリーダーでの確認:
- VoiceOver (macOS/iOS), NVDA (Windows), JAWS (Windows) などのスクリーンリーダーを起動します。
- Tabキーでアコーディオンのタイトル(ボタン)に移動した際に、「セクション1のタイトル、折りたたみ済み、ボタン」(または同様の情報)のように、ボタンのラベルと開閉状態(折りたたみ済み/展開済み)が正しく読み上げられるか確認します。
- Enter/Spaceキーで開閉操作を行った後、再度ボタンにフォーカスを戻すか、ページを移動して、状態の変化(「展開済み」への変化)が読み上げられるか確認します。
- アコーディオンが開いた状態で、内容パネル内のコンテンツが正しく読み上げられるか確認します。
- アクセシビリティ評価ツールの利用:
- ブラウザ拡張機能(Lighthouse, axe DevTools, WAVEなど)や、オンラインツールを使用して、ARIA属性の誤りやその他のアクセシビリティに関する問題を自動的にチェックします。ただし、これらのツールは全ての問題を検出できるわけではないため、手動での確認が不可欠です。
まとめ
アクセシブルなアコーディオンコンポーネントの実装は、HTMLのセマンティクス、適切なARIA属性の使用、そしてJavaScriptによるキーボード操作への対応が鍵となります。button
要素をトリガーとし、aria-expanded
と aria-controls
を正しく設定し、キーボードフォーカスと視覚的な状態表示を明確にすることで、より多くのユーザーが快適に利用できるアコーディオンを提供できます。
本記事で紹介した手順とコード例を参考に、ご自身のプロジェクトにアクセシブルなアコーディオン実装を取り入れていただければ幸いです。どのようなコンポーネントにおいても、利用者の視点に立ち、様々な操作方法や環境を想定して設計・実装することが、アクセシビリティ対応の第一歩となります。