スクリーンリーダーに優しいサイト作り:WAI-ARIA属性の実践テクニック

WAI-ARIA(ワイ・エイ・アイ・エイリア)は、「Web Accessibility Initiative – Accessible Rich Internet Applications」の略称で、直訳すると「ウェブアクセシビリティ・イニシアチブ ― アクセシブルなリッチインターネットアプリケーション」という意味になります。簡単に言うと、WAI-ARIAとは、障がいのあるユーザーを含むすべての人がWebアプリケーションやWebサイトを利用しやすくするために、HTMLに「意味の情報」や「状態の情報」を追加するためのルールや属性の集合のことを指します。

WAI-ARIAとは何か

ここで出てきた「アクセシビリティ」とは、年齢や身体的な状態、利用しているデバイスや環境に関わらず、誰もが情報や機能にアクセスしやすい状態を目指す考え方です。視覚障がいのあるユーザーはスクリーンリーダーと呼ばれる音声読み上げソフトを使ってWebページを利用することがありますが、画面に「見た目として」ボタンやダイアログが表示されていても、コードに意味が正しく記述されていないと、その存在や状態がうまく伝わりません。WAI-ARIAは、そうした支援技術に「これはボタンです」「これは現在開いているタブです」「この要素はエラー状態です」といった情報を伝えるための仕組みです。

従来のHTMLは、文書型のページを前提に作られていましたが、近年のWebはボタンで画面が切り替わったり、ページ遷移無しでモーダル(画面の上に重なるダイアログ)が表示されたりするなど、アプリケーションに近い挙動をすることが増えています。このような「リッチなインターフェイス」は、見た目には便利に見えても、そのままでは支援技術にとって非常に分かりづらいことがあります。例えば、見た目はボタンでも実際のコードが単なる<div>タグだと、スクリーンリーダーはそれをボタンとして認識できません。このギャップを埋めるために生まれたのがWAI-ARIAです。

WAI-ARIAで行うことは大きく分けて次の3つです。

  • 要素の「ロール(role)」を伝える
    「この要素はボタン」「この要素はナビゲーション」「この要素はタブパネル」といった役割を指定することです。role属性を使って指定します。
  • 要素の「状態(state)」を伝える
    「このチェックボックスはチェックされている」「このメニューは展開中」「このタブは選択されている」といった状態を、aria-checkedaria-expandedなどの属性で伝えます。
  • 要素の「プロパティ(property)」を伝える
    「このフォームには必須入力項目がある」「この要素は別の要素のラベルである」といった情報を、aria-requiredaria-labelledbyなどの属性で指定します。

ここで出てきたaria-で始まる属性群をまとめて「ARIA属性」と呼びます。これらは見た目のスタイルを変えるものではなく、あくまで支援技術に向けた「意味の情報」「状態の情報」を補足するためのものです。ユーザーによっては、まったく見た目の変化を感じないかもしれませんが、スクリーンリーダーの利用者にとっては、ページが理解しやすくなる重要な手がかりになります。

ただし、WAI-ARIAは「なんでもかんでも付ければよい魔法の道具」ではなく、「まずはHTML本来の要素(ネイティブ要素)を正しく使うこと」が前提になります。例えば、クリックできる要素には本来<button>タグを使うべきであり、わざわざ<div>role="button"を付ける必要はありません。HTMLがもともと持っているアクセシビリティ機能を活かし、そのうえでどうしても不足する部分をWAI-ARIAで補うという考え方が重要です。よく言われるガイドラインとして、「使わなくて済むならWAI-ARIAを使わないほうが良い」というものがあり、これはHTMLのセマンティクス(意味づけ)を優先する姿勢を表しています。

WAI-ARIAは、特にJavaScriptでリッチなUIコンポーネントを自作するときに力を発揮します。例えば、タブUI、アコーディオンメニュー、モーダルダイアログ、カスタムセレクトボックスなどです。これらは標準のHTML要素だけでは表現しきれないことが多く、「今どのタブがアクティブなのか」「ダイアログが表示中かどうか」「メニューが開いているか閉じているか」といった情報を明示的に伝える必要があります。ここで、role="tab", role="dialog", aria-selected="true", aria-expanded="false"などを適切に設定することで、スクリーンリーダーがコンポーネントの構造や状態を正しく理解できるようになります。

また、WAI-ARIAは単にコードの書き方だけではなく、「どのようなインターフェイス設計がユーザーにとって分かりやすいか」という、設計思想にも深く関係しています。キーボード操作だけで全ての機能にアクセスできるようにすること、フォーカスの移動先を分かりやすくコントロールすること、画面が変わったときにユーザーにそれを伝えることなど、多くのポイントが関わってきます。WAI-ARIAは、そのうち「支援技術にどう伝えるか」という部分を担当する仕様だと捉えると理解しやすくなります。

このように、WAI-ARIAとは、現代的なWebアプリケーションのインターフェイスを、より多くのユーザーにとって利用しやすくするための重要な仕組みであり、特にフロントエンド開発に関わるエンジニアにとっては欠かせない知識のひとつです。

WAI-ARIAで使用されるrole属性の基礎

WAI-ARIAで最もよく登場するのが、role(ロール)属性です。roleとは、日本語にすると「役割」という意味で、その要素がユーザーインターフェイスの中でどんな役割を持っているのかを、支援技術に伝えるための情報です。見た目がボタンでもHTML上は<div>だったり、見た目がメニューでも実際は<ul>ではなかったりする場合に、「これはボタンです」「これはナビゲーションです」と明示するために使います。

もともとHTMLの要素には「暗黙のロール(implicit role)」と呼ばれる、標準で決まっている役割があります。たとえば<button>は自動的にrole="button"<a href="...">role="link"という意味を持ちます。この場合、わざわざrole属性を書かなくても、スクリーンリーダーなどはボタンやリンクとして認識してくれます。逆に言うと、こうしたネイティブ要素を優先的に使うことが、アクセシビリティ的には望ましいとされています。

role属性が本領を発揮するのは、ネイティブ要素では表現しにくい複雑なUIコンポーネントを自作するときです。たとえば、JavaScriptでタブUIを作るとき、多くの場合は<div><button><li>などを組み合わせて独自の構造を作ります。見た目だけでは「これはタブです」と支援技術に伝わらないため、role="tab", role="tablist", role="tabpanel"といったロールを指定することで、「タブ一式であること」「どれがタブ本体か」「どれがタブの中身か」をはっきり伝えられます。

role属性は、大きく分けて次のようなカテゴリに分類できます。

  • ランドマークロール(landmark)
    ページ内の主要な領域を示すロールです。role="banner"(ページ上部のヘッダー)、role="navigation"(ナビゲーション領域)、role="main"(本文の主要部分)、role="contentinfo"(フッターなど)、role="complementary"(補足的なコンテンツ)などがあります。スクリーンリーダーはこれらのランドマークをショートカットで飛び回ることができるため、ページの中を効率よく移動できるようになります。
  • ウィジェットロール(widget)
    ボタン、チェックボックス、スライダー、タブ、メニューなど、ユーザーが操作する部品に対するロールです。role="button", role="checkbox", role="switch", role="slider", role="tab", role="dialog"などが含まれます。これらはキーボード操作や状態管理と組み合わせて使うことが多く、「押せる」「切り替えられる」といった性質を支援技術に伝えるために重要です。
  • ドキュメント構造ロール(document structure)
    文書内の見出しやセクション構造を示すロールです。role="heading", role="article", role="list", role="listitem"などがあります。ただし、見出しであれば本来は<h1><h6>、リストであれば<ul>,<ol>,<li>を使った方がよく、あくまでHTMLだけでは表現が難しいケースを補う位置づけになります。
  • その他の補助的なロール
    role="alert"(即座にユーザーに知らせたいメッセージ)、role="status"(状態表示)、role="tooltip"(ツールチップ)、role="progressbar"(進捗バー)など、特定の意味を持つロールが多数用意されています。

実際のコード例を見てみます。たとえば、<div>で作られたカスタムボタンにロールを与える場合です。

<div role="button" tabindex="0">
  メニューを開く
</div>

ここではrole="button"によって、この要素がボタンとして扱われるべきであることを示し、tabindex="0"によってキーボードフォーカスを当てられるようにしています。さらにJavaScript側でEnterキーやSpaceキーを押したときの動作を実装することで、支援技術からも「ボタンとして」利用できるUIになります。

一方で、<button>要素を使える場面であえて<div role="button">を使う必要はありません。ネイティブなボタンは、自動的にキーボード操作やロール、状態などが正しく扱われるためです。このように、「HTMLの標準機能で足りるところは標準を使い、どうしても不足するときだけroleで補う」という考え方が大切になります。

role属性を使う際の注意点として、元々ロールを持っている要素に対して、むやみに上書きをしないことがあります。たとえば、<nav>要素には暗黙的にナビゲーションの意味があるため、あえてrole="navigation"を付けなくても問題なく認識されます。逆に、意味の違うロールを付けてしまうと、HTMLとWAI-ARIAの情報が矛盾し、支援技術側での解釈が不自然になる可能性があります。

また、role属性には「組み合わせ」や「親子関係」が定義されているものも多くあります。たとえば、role="menu"の子要素にはrole="menuitem"などが必要といったルールです。ロールを1つだけ見るのではなく、「このロールはどのロールとセットで使うとよいのか」「親子関係はどうなっているのか」という視点も重要です。

role属性は、HTMLの見た目を変えるためではなく、「このUIは何者なのか」を機械的に説明するためのラベルのようなものです。どのロールがどのような意味を持ち、どの要素に付けるのが自然なのかを理解しておくと、アクセシビリティを意識した設計と実装が進めやすくなります。

aria-*属性の種類と役割

WAI-ARIAでは、aria-から始まる多くの属性が定義されています。これらはまとめて「ARIA属性」と呼ばれ、「その要素がどんな状態なのか」「どの要素とどの要素が関係しているのか」といった情報を、支援技術に伝えるために使われます。ここでいう支援技術とは、スクリーンリーダー(画面内容を音声で読み上げるソフト)や点字ディスプレイなど、障がいのある方をサポートするためのツールのことです。ARIA属性は見た目を変えるためのものではなく、「裏側の説明書き」をHTMLに追加するようなイメージで捉えていただくと分かりやすいです。

ARIA属性は大きく「状態(state)」と「プロパティ(property)」の2つのグループに分類されます。状態は時間とともに変化するもの、プロパティは基本的に固定の情報を表します。たとえば、チェックボックスがオンかオフかは状態なのでaria-checkedで表し、その項目が必須入力かどうかはプロパティなのでaria-requiredで表す、といった考え方です。どちらもHTML属性として記述しますが、「変わるものかどうか」を意識して使い分ける点がポイントになります。

まず、よく使われるプロパティから見ていきます。

  • aria-label
    画面上にテキストが表示されていないボタンやアイコンなどに、「スクリーンリーダー向けのラベル」を付けるための属性です。たとえば、ゴミ箱アイコンだけが表示された削除ボタンの場合、aria-label="削除"のように指定します。こうすることで、視覚的にはアイコンのみでも、スクリーンリーダーは「削除 ボタン」と読み上げられるようになります。
  • aria-labelledby
    すでに画面上に表示されている別の要素をラベルとして利用したいときに使います。値には、ラベルとして使いたい要素のidを指定します。例えば、<h2 id="dialog-title">設定</h2>という見出しをダイアログのタイトルとして使う場合、そのダイアログの要素にaria-labelledby="dialog-title"を付けると、「このダイアログの名前は『設定』です」と支援技術に伝わります。
  • aria-describedby
    ラベルとは別に、その要素に関する補足説明がある場合に使います。エラーメッセージや注意書きなど、ユーザーに追加情報を伝えたいときに、説明テキストのidを指定して紐づけます。スクリーンリーダーは、ラベルと合わせてこの説明も読み上げるようになります。
  • aria-required
    フォームの入力項目が必須かどうかを示すプロパティです。aria-required="true"とすると、「必須の入力欄」として支援技術に伝わります。見た目の「※必須」表示とは別に、機械的にも必須であることを知らせる役割を持ちます。

次に、代表的な状態を表すARIA属性を見ていきます。

  • aria-checked
    チェックボックスやトグルボタンのオン・オフ状態を表します。true(オン)、false(オフ)、mixed(どちらでもない中間状態)の3つの値を取ります。JavaScriptで状態を切り替えるときは、この値も必ず一緒に更新する必要があります。
  • aria-selected
    タブ、リスト項目、メニュー項目など、「選択されているかどうか」を表すときに使います。aria-selected="true"の要素が現在選ばれていることを示し、falseは選ばれていない状態を表します。見た目でハイライトを切り替えるだけでなく、この属性も同期させることでスクリーンリーダーにも選択状態が伝わります。
  • aria-expanded
    アコーディオンやドロップダウンメニューなど、「開いているか閉じているか」が大事なUIで使います。trueなら展開中、falseなら閉じている状態を意味します。見た目でコンテンツを開閉させるだけでなく、トリガーとなるボタンや見出しにaria-expandedを付けておくと、支援技術が状態変化を理解しやすくなります。
  • aria-disabled
    ボタンやリンクなどが現在操作できるかどうかを示します。trueで無効、falseで有効です。HTMLのdisabled属性と似ていますが、disabledが使えない要素に対しても「今は操作できません」と伝えたい場合に利用されます。
  • aria-invalid
    入力内容が不正な状態であることを示すための属性です。フォームのバリデーションでエラーが出たときにaria-invalid="true"を付けると、支援技術が「この項目はエラー状態である」と認識できるようになります。

さらに、動的なコンテンツの変化を知らせるためのARIA属性もあります。

  • aria-live
    ページの一部が自動的に書き換わるような場合に、その更新内容をスクリーンリーダーに知らせるための仕組みです。aria-live="polite"aria-live="assertive"などを指定することで、「落ち着いたタイミングで読んでほしいのか」「すぐに読み上げてほしいのか」といった優先度を調整できます。通知領域やチャットメッセージ、新着情報のエリアなどに使われます。
  • aria-busy
    要素が現在読み込み中・処理中であることを表します。aria-busy="true"にしておき、処理が終わったらfalseに戻します。スクリーンリーダーはこれを手がかりに、「まだ準備中かどうか」を判断できます。

これらのARIA属性には、「どの要素に付けてよいか」「どんな値を取るべきか」といった細かいルールが定義されています。すべてを一度に覚える必要はありませんが、実装するときには「このUIの状態や関係性を、支援技術にどう伝えたいか」を意識しながら、必要な属性だけを選んで使うのが良いです。特に、JavaScriptで状態を変更したときにARIA属性の値を更新し忘れると、見た目と支援技術側の情報がズレてしまうため、実装時には「UIの状態とARIAの値を常に同期させる」ことが重要になります。

対話型UIコンポーネントにおけるWAI-ARIAの活用

対話型UIコンポーネントとは、ユーザーがクリック・キーボード操作・ジェスチャーなどによって直接操作する部品のことを指します。具体的には、モーダルダイアログ、タブ、アコーディオン、ドロップダウンメニュー、トグルスイッチ、カスタムセレクトボックスなどが挙げられます。これらは見た目や動きがリッチである一方、HTMLの標準要素だけでは「何が起きているのか」「どの状態なのか」が支援技術に伝わりにくいことが多くあります。そこで重要になるのが、WAI-ARIAによる補足情報の付与です。

モーダルダイアログでのWAI-ARIA

モーダルダイアログは、画面の中央に重なって表示され、背後のコンテンツよりも優先して操作させたいときに使われるコンポーネントです。例えば「設定」「確認」「警告」のような場面で利用されます。このとき、視覚的にはダイアログが前面に出ていますが、スクリーンリーダーにとっては「今どこにフォーカスがあるのか」「どの要素が重要なのか」が分かりにくくなります。

代表的な設定としては次のようなものがあります。

  • ダイアログのコンテナ要素にrole="dialog"またはrole="alertdialog"を付ける
  • タイトルとなる見出しのidを用意し、ダイアログにaria-labelledbyで関連付ける
  • 必要に応じて本文説明の要素とaria-describedbyを紐づける
<div role="dialog" aria-labelledby="dialog-title" aria-describedby="dialog-desc">
  <h2 id="dialog-title">設定の確認</h2>
  <p id="dialog-desc">変更内容を確認し、保存するかキャンセルを選択してください。</p>
  <button>保存</button>
  <button>キャンセル</button>
</div>

これにより、スクリーンリーダーはダイアログにフォーカスが移動した際に「設定の確認 ダイアログ」というような形で読み上げ、ユーザーに今どのコンポーネントを操作しているのかを伝えやすくなります。加えて、JavaScriptでフォーカスのトラップ(ダイアログ内にフォーカスを閉じ込める制御)を行うことで、キーボード操作でもダイアログから意図せず抜け出さないようにできます。

タブUIでのWAI-ARIA

タブUIは、複数のコンテンツを切り替えて表示するためのコンポーネントです。視覚的にはタブが横に並び、選択しているタブの内容だけが下に表示されます。支援技術にとっては「どれがタブか」「選択中のタブはどれか」「タブを切り替えたときにどのコンテンツが対応しているか」を明確にする必要があります。

タブUIでは、次のようなロールとARIA属性を組み合わせます。

  • タブ一覧を囲む要素にrole="tablist"
  • 各タブボタンにrole="tab"
  • 各タブに対応するコンテンツ領域にrole="tabpanel"
  • 選択中のタブにaria-selected="true"、それ以外にaria-selected="false"
  • タブとタブパネルをaria-controlsidで対応付ける
<div role="tablist">
  <button role="tab" id="tab1" aria-selected="true" aria-controls="panel1">基本設定</button>
  <button role="tab" id="tab2" aria-selected="false" aria-controls="panel2">詳細設定</button>
</div>

<div id="panel1" role="tabpanel" aria-labelledby="tab1">
  <!-- 基本設定の内容 -->
</div>
<div id="panel2" role="tabpanel" aria-labelledby="tab2" hidden>
  <!-- 詳細設定の内容 -->
</div>

JavaScriptでタブを切り替える際には、見た目の切り替えと一緒にaria-selectedの値やhidden属性の有無を更新し、キーボード操作(左右矢印キーなど)でタブを移動できるようにしておくことが重要です。

アコーディオン・ドロップダウンでのWAI-ARIA

アコーディオンやドロップダウンメニューでは、「開いているか・閉じているか」という状態を伝える必要があります。このとき頻繁に使われるのがaria-expanded属性です。aria-expandedは、要素が展開されているかどうかを真偽値で示す属性で、トリガーとなるボタンに付けるのが一般的です。

<button aria-expanded="false" aria-controls="section1" id="accordion-btn-1">
  詳細を表示
</button>
<div id="section1" role="region" aria-labelledby="accordion-btn-1" hidden>
  アコーディオンの内容
</div>

ユーザーがボタンを押すと、JavaScriptでaria-expandedの値をtrueに切り替え、hiddenを外してコンテンツを表示します。再度押すと元に戻します。このように状態変化とARIA属性を常に連動させることで、スクリーンリーダーは「展開」「折りたたみ」の変化を正しく理解することができます。

カスタムボタン・トグルスイッチでのWAI-ARIA

デザイン上の理由から、<button>要素ではなく<div><span>でボタン風のパーツを作るケースもあります。この場合、role="button"aria-pressedなどを用いて意味を補う必要があります。aria-pressedは、オン・オフを切り替えるボタン(トグルボタン)の状態を表すためのARIA属性です。

<div role="button" aria-pressed="false" tabindex="0">
  通知を有効にする
</div>

オンのときにaria-pressed="true"へ切り替えることで、「現在有効かどうか」が支援技術に伝わります。同時に、キーボード操作(Enterキー・スペースキー)でも切り替えられるようにイベント処理を実装します。

カスタムセレクトボックスでのWAI-ARIA

ブラウザ標準の<select>を使わずに、独自デザインのセレクトボックスを実装する場合、リストボックスやコンボボックスに関するロールやARIA属性を組み合わせます。例えば、role="combobox"role="listbox"role="option"などです。選択状態はaria-selected、展開状態はaria-expandedで表します。

<div role="combobox" aria-expanded="false" aria-owns="color-list" aria-haspopup="listbox">
  <input type="text" aria-autocomplete="list" />
</div>
<ul id="color-list" role="listbox">
  <li role="option" aria-selected="true">赤</li>
  <li role="option" aria-selected="false">青</li>
</ul>

このようなコンポーネントは仕様も複雑ですが、その分WAI-ARIAの設計が重要になり、状態・選択中の項目・展開範囲などを細かく伝える必要があります。

対話型UIコンポーネントにWAI-ARIAを適用する際には、見た目のデザインや動きだけでなく、「このコンポーネントは何の役割で、どのような状態を持ち、ユーザーの操作に応じて何が変わるのか」を整理し、それをロールとARIA属性で一貫して表現することが求められます。

WAI-ARIAとHTMLのセマンティクスの関係

WAI-ARIAを理解するときにとても重要なのが、「HTMLのセマンティクス」との関係です。セマンティクス(semantics)とは「意味づけ」という意味で、HTMLにおいては「このタグは文章の中でどんな意味・役割を持つのか」という情報を表します。たとえば、<h1>はページの主見出し、<nav>はナビゲーション、<main>は主な内容部分、<button>は押せるボタンという意味を持ちます。これらのタグは、単に見た目を変えるためではなく、ブラウザや支援技術に対して文書構造を伝えるためのものです。

HTMLのセマンティクスは、実はWAI-ARIAの前から存在している「アクセシビリティの土台」です。多くの要素には、暗黙のロール(implicit role)が割り当てられています。暗黙のロールとは、タグを書いただけで自動的に決まる役割のことです。たとえば、<button>は何も属性を付けなくてもrole="button"として扱われ、<a href="...">role="link"として扱われます。<nav>はナビゲーション領域として扱われ、<header><footer>にもそれぞれ意味があります。このように、HTML本来の要素をきちんと使うだけで、かなりのアクセシビリティが確保されます。

WAI-ARIAは、この既存のセマンティクスを「補う」ための仕組みです。つまり、本来のHTMLだけでは表現できない複雑なUIコンポーネントや、JavaScriptで動的に変化する状態を支援技術に伝えるために登場したものです。この関係性を誤解して、何もかもにrolearia-*属性を付けてしまうと、かえって情報が過剰になったり、HTMLとARIAの意味が矛盾したりして、支援技術が混乱する可能性があります。

ここでよく使われる指針が、「First rule of ARIA(ARIA第一原則)」と呼ばれる考え方です。内容としては、「使わなくてよいならARIAを使わないこと」という趣旨です。つまり、HTMLのセマンティックな要素だけで十分に意味が伝わるなら、わざわざWAI-ARIAを追加する必要はないということです。たとえば、シンプルなボタンには<button>、リンクには<a>、リストには<ul><li>など、元々あるタグを正しく使うことで、スクリーンリーダーは自然に内容を理解できます。

逆に、WAI-ARIAが必要になるのは、次のような場面です。

  • 単なる<div><span>を使って、カスタムのボタンやタブ、スイッチなどを実装している
  • シングルページアプリケーションのように、URLが変わらずに中身だけ書き換わるインターフェイス
  • 標準の<select>ではなく、自作したプルダウンやオートコンプリート入力を使っている
  • モーダルダイアログやトースト通知など、動的に現れたり消えたりするUIを扱っている

このような場面では、HTMLのセマンティクスだけでは十分な情報が得られないため、WAI-ARIAで「これはタブです」「この要素はダイアログです」「この項目は選択済みです」といった情報を追加します。

HTMLのセマンティクスとWAI-ARIAの関係を整理すると、次のようなイメージになります。

  • HTMLのセマンティクス
  • 文書の骨格や役割を表す「土台」
  • できるだけネイティブ要素で意味を表現する
  • 多くの場合、これだけで十分なアクセシビリティを提供できる
  • WAI-ARIA
  • 土台だけでは足りない部分を補うための「補強材」
  • 特にリッチなUIコンポーネントや動的な状態変化を説明する役割
  • 正しく使わないと、かえって意味が分かりづらくなる可能性もある

たとえば、<nav>要素はそれ自体にナビゲーションとしての意味があるため、role="navigation"を重ねて書く必要はありません。同様に、<main>role="main"<button>role="button"を重ねるのも基本的には不要です。これらはHTML側のセマンティクスがすでに機能しているためです。二重に指定しても多くの環境では大きな問題にならないことが多いですが、「どちらが正しいのか」という情報が複雑になりやすく、将来の仕様変更や支援技術の挙動に影響する可能性もあります。

一方で、<div><span>には特定の意味がありません。これらは「汎用コンテナ」と呼ばれ、レイアウトやスタイルのためによく使われますが、アクセシビリティ的には中立的な存在です。こうした要素をボタンやタブのように振る舞わせたい場合は、rolearia-*属性を使って意味と状態を補う必要があります。ここで初めてWAI-ARIAの出番が訪れます。

また、HTMLのセマンティクスとWAI-ARIAを両方使う場面として、「構造はHTML、状態はARIA」という分担もよくあります。たとえば、<button>に対してaria-pressedを付けてトグルボタンの状態を表現したり、<ul><li>で作ったメニューにaria-expandedを組み合わせて開閉状態を示したりするケースです。この場合、ベースとなる構造はHTMLの意味で伝え、変化する部分だけARIAで補足するという役割分担になっています。

さらに、HTMLのセマンティクスは検索エンジンや他のツールにも影響を与えます。見出しやリスト、テーブル、フォームなどを正しくマークアップすると、検索結果の理解や自動解析も改善されます。WAI-ARIAは主に支援技術向けの情報ですが、HTMLのセマンティクスと矛盾しないよう慎重に設計することで、全体として一貫性のある構造を保てます。

HTMLとWAI-ARIAの関係を意識することで、「何でもARIAで解決しようとする」のではなく、「まずHTMLで意味を表し、それでも足りないところにだけARIAを使う」というバランス感覚が身についてきます。これが、アクセシブルなWebインターフェイスを設計・実装するうえでの基本的な考え方です。

スクリーンリーダーがどのようにWAI-ARIAを解釈するか

スクリーンリーダーは、画面に表示されているものをそのまま「見た目の通り」に読んでいるわけではありません。実際には、ブラウザが内部で持っている「アクセシビリティツリー」と呼ばれる情報を基にして、要素の種類(ロール)、名前(ラベル)、状態(オン・オフなど)、関係性(どの要素と紐づいているか)を解釈し、それを音声や点字でユーザーに伝えています。WAI-ARIAは、このアクセシビリティツリーに載る情報を補強するための仕組みであり、スクリーンリーダーはこの情報を積極的に参照します。

ブラウザは、HTMLを読み込むときにまず「DOMツリー」という構造を作ります。DOMツリーは、タグ同士の親子関係を表した構造です。そのうえで、DOMツリーをもとに「アクセシビリティツリー」を生成します。このとき、<button>はボタンとして、<a>はリンクとして、<h1>は見出しとして、といった具合に、それぞれの要素に応じたロールや名前、状態が自動的に設定されます。ここに、WAI-ARIAのrolearia-*属性が指定されている場合、それらもアクセシビリティツリーに反映され、スクリーンリーダーが利用する情報になります。

スクリーンリーダーが要素を読み上げるとき、主に次のような情報を組み合わせます。

  • ロール(role)
    「ボタン」「リンク」「見出し」「タブ」など、その要素が持つ役割です。ユーザーはこの情報から、「これは押せるのか」「移動の目印になるのか」などを判断します。role="button"role="dialog"などの指定は、このロールに影響を与えます。
  • 名前(accessible name)
    要素に付けられているラベルのことです。通常は要素のテキスト内容や<label>の対応、alt属性、aria-labelaria-labelledbyなどから決まります。たとえば、aria-label="メニューを開く"が付いたボタンであれば、「メニューを開く ボタン」といった形で読み上げられます。
  • 状態・プロパティ(state / property)
    aria-checkedaria-expandedaria-selectedaria-disabledなどによって表される「今どういう状態か」という情報です。スクリーンリーダーはこれらを読み上げることで、「チェック済み」「展開」「選択中」「無効」など、視覚的にはアイコンや色で示されている情報を音声でも伝えます。

スクリーンリーダーには、主に2つの操作モードがあります。1つは「ブラウズモード(仮想カーソルモードとも呼ばれます)」で、もう1つは「フォーカスモード(フォームモードなど)」です。ブラウズモードでは、ユーザーは上下矢印キーなどを使ってページ内を移動し、スクリーンリーダーがアクセシビリティツリーをなぞりながら、「見出し」「段落」「リンク」「ボタン」などを順に読み上げます。このとき、WAI-ARIAのロールやラベル情報があると、「これは何か」「今どの要素にいるか」が明確になり、効率よく移動できます。

フォーカスモードでは、フォーム入力やボタン操作など、インタラクティブな要素の操作に特化した挙動になります。ここでは、キーボードのスペースキーやEnterキー、矢印キーなどが、その要素を操作するための入力として扱われます。JavaScriptでカスタムコンポーネントを実装する際に、role="button"tabindex="0", aria-pressedなどを適切に設定すると、スクリーンリーダーはフォーカスモードでその要素を「押せるボタン」として扱ってくれます。

また、スクリーンリーダーはショートカットキーを使って、特定のロールを持つ要素だけを素早く巡回する機能を持っています。たとえば、「見出しだけを順番に移動する」「フォームコントロールだけを飛び回る」「ランドマークロール(role="navigation", role="main"など)だけを移動する」などです。ランドマークロールや見出し、リストなどを正しくマークアップしておくと、ユーザーは長いページの中でも目的の場所に素早く到達できます。

動的なコンテンツの変化についても、スクリーンリーダーはWAI-ARIAを参照します。aria-liveでマークされた領域は、「ここは自動的に内容が変わる可能性があるので、そのときは読み上げてください」という合図になります。aria-live="polite"であれば、現在の読み上げが一段落するタイミングで通知され、aria-live="assertive"であれば、より優先度が高く扱われます。チャットメッセージ、新着通知、エラーメッセージなどをユーザーに気づいてもらいたいときに重要です。

一方で、スクリーンリーダーごとにWAI-ARIAの解釈や読み上げの細かい挙動は異なることがあります。あるスクリーンリーダーでは詳細な状態を読み上げる一方、別のスクリーンリーダーでは簡略化して読み上げる場合もあります。そのため、実務では1種類だけでなく、複数のスクリーンリーダー・複数のブラウザで挙動を確認することが望ましいとされています。ただし、学習段階では、「アクセシビリティツリーに乗る情報をきちんと構造化する」「ロール・名前・状態を整える」という考え方を押さえておくことが大切です。

なお、WAI-ARIAで指定した情報が常に最優先されるわけではなく、HTMLのセマンティクスやブラウザの実装との組み合わせで最終的な挙動が決まります。意味が重複していたり矛盾していたりすると、スクリーンリーダー側での処理が複雑になり、期待通りに読まれないこともあります。そのため、WAI-ARIAは「不足を補うもの」として慎重に使い、HTMLと整合性のある設計を心がけることが重要になります。

実践で役立つWAI-ARIAの実装ポイント

WAI-ARIAを実務で使うときにまず大切なのは、「HTMLだけで実現できないところにだけ使う」という考え方です。ボタンであれば<button>、リンクであれば<a>、フォームの入力であれば<input><textarea>といったように、ネイティブ要素(ブラウザが標準で意味を知っているタグ)を優先して使います。ネイティブ要素は、すでにロール・キーボード操作・フォーカス移動などが組み込まれているため、わざわざrole="button"などを追加する必要がありません。WAI-ARIAは、どうしても<div><span>でカスタムコンポーネントを作らざるを得ない場面で、「意味」と「状態」を補うための道具として位置づけると扱いやすくなります。

次に意識したいのが、「ロール・名前・状態」の3点セットを常に意識することです。ロールとはrole属性による役割(ボタン、タブ、ダイアログなど)、名前とはaria-labelaria-labelledbyなどによって与えられるラベル、状態とはaria-checkedaria-expandedなどで表現されるオン・オフや開閉の情報です。実際にコンポーネントを設計するとき、「この部品は何として認識されるべきか(ボタンか、タブかなど)」「その名前はどこから取るか(ラベルのテキストか、アイコンの説明か)」「どんな状態を持ち、その変化をどう表現するか」を紙に書き出して整理しておくと、必要なARIA属性が自然と見えてきます。

JavaScriptで状態を切り替えるコンポーネントでは、「見た目の状態」と「ARIAの状態」を必ず同期させることが重要です。例えば、アコーディオンの開閉ボタンを作る場合、「開いているときはアイコンをマイナスにする」「閉じているときはプラスにする」といった視覚的な変化だけでなく、aria-expandedの値もtruefalseで切り替えます。チェックボックス風のトグルボタンであれば、見た目のON/OFFと一緒にaria-pressedaria-checkedも更新します。この同期が崩れると、見えている状態と読み上げられる状態が食い違い、支援技術の利用者が誤解してしまう原因になります。

キーボード操作への対応も実装ポイントのひとつです。tabindex属性を使ってフォーカス可能にするだけでなく、「Enterキーやスペースキーでボタンが押せるか」「上下左右キーでタブやメニュー内を移動できるか」といった操作体系を検討します。特に、role="button"を付けた<div>などは、そのままではキーボードで何も起きないため、キーイベントを実装して初めて「本物のボタン」として機能します。WAI-ARIAだけでは操作性は改善されないため、「ロールと操作をセットで設計する」という意識が有効です。

フォーカス管理も現場でつまずきやすいポイントです。モーダルダイアログを表示するときは、開いた瞬間にダイアログ内の最初の操作対象(例:閉じるボタンやタイトル)にフォーカスを移動し、ダイアログを閉じるときには元のボタンなどにフォーカスを戻します。また、ダイアログ表示中はタブキー操作で背後のコンテンツにフォーカスが移動しないよう、ダイアログ内にフォーカスを閉じ込める制御も必要です。これらはWAI-ARIAだけでは実現できず、JavaScriptでのフォーカス制御と組み合わせることで、初めて使いやすい体験になります。

フォームのエラー表示では、ARIA属性を使って「何が問題なのか」を明確に伝えることができます。エラーのある入力欄にはaria-invalid="true"を付与し、エラーメッセージの要素にidを設定しておきます。そして、そのidを入力欄のaria-describedbyに指定すると、スクリーンリーダーは入力欄を読み上げる際にエラー内容も併せて読み上げることができます。必須項目にはaria-required="true"を使うことで、「ここは必須です」という情報を機械的にも伝えることができます。

ラベル付けに関しては、aria-labelaria-labelledbyの使い分けも実務でよく登場します。シンプルなアイコンボタンなど、画面上に文字がない場合はaria-labelで名前を直接埋め込むと分かりやすくなります。一方、すでに視覚的なテキストが存在し、それをそのままスクリーンリーダーにも使わせたい場合はaria-labelledbyを用いて、そのテキスト要素のidと関連付けると、ラベルが1か所で管理できて保守もしやすくなります。

また、実装方針として「パターンを再利用する」ことも重要です。タブ、アコーディオン、モーダルといったよくあるコンポーネントは、毎回一から設計するのではなく、一度ロールやARIA属性、キーボード操作、フォーカス制御まで含めて「ひな形コンポーネント」を作っておき、プロジェクト内で共通利用すると品質が安定します。このとき、コンポーネントの使い方を簡単なコメントやドキュメントとしてチーム内で共有しておくと、他のメンバーもWAI-ARIAの意図を理解しながら使えるようになります。

最後に、実装後の確認として、視覚に頼らずにキーボードだけで操作してみること、ブラウザの開発者ツールなどでアクセシビリティ情報(ロール・名前・状態)を確認することが実践的なチェック方法として役立ちます。画面を見ながら「ここはこう読まれてほしい」と想像するだけでなく、実際に環境を切り替えて動作を見ることで、WAI-ARIAの設計と実装が意図通りになっているかを確認できます。

まとめ

この記事では、WAI-ARIAをテーマに、基礎的な概念から具体的な属性、対話型UIコンポーネントへの適用方法、HTMLのセマンティクスとの関係、スクリーンリーダー側での解釈、そして実務で意識したい実装ポイントまでを段階的に整理しました。全体を通して共通しているキーワードは「ロール(役割)」「名前(ラベル)」「状態(ステート)」の3つであり、これらを正しく組み立てることで、支援技術ユーザーにとって分かりやすいインターフェイスを作れる、という点が軸になっています。

まず「WAI-ARIAとは何か」という観点では、WAI-ARIAがHTMLの上に追加される「意味と状態の説明書き」のような存在であることを確認しました。特に、モーダルダイアログやタブ、アコーディオンなど、JavaScriptを用いたリッチなUIは、見た目だけでは支援技術に十分な情報が伝わらないため、WAI-ARIAで役割や状態を補足する必要があることを学びました。一方で、WAI-ARIAは魔法の道具ではなく、あくまでも「足りない部分を補う仕組み」であり、HTML自体の設計が不十分な状態を無理に隠すものではないという視点も重要です。

次に、role属性とaria-*属性について、それぞれの役割を整理しました。role属性は「この要素はボタン」「ここはタブの集合」「ここはナビゲーション」といった役割を支援技術に伝えるためのラベルとして機能し、ランドマークロールやウィジェットロールなど、いくつかのカテゴリが存在することを見てきました。一方、aria-labelaria-labelledbyaria-describedbyといった属性は「この要素の名前は何か」「どのテキストが説明として関連しているか」を示すために使われ、aria-checkedaria-expandedaria-selectedなどは「今どんな状態か」を動的に伝えるために利用されます。JavaScriptでUIの状態を変えるときには、これらのARIA属性の値も一緒に更新することで、見た目と読み上げ内容を一致させることができる、というのが実装の重要なポイントでした。

対話型UIコンポーネントにおける活用では、モーダルダイアログ・タブ・アコーディオン・カスタムボタン・カスタムセレクトボックスなどを例に、どのロールと属性を組み合わせるかを整理しました。モーダルにはrole="dialog"aria-labelledby、タブにはrole="tablist", role="tab", role="tabpanel"aria-selected、アコーディオンにはaria-expandedaria-controls、トグルボタンにはaria-pressedaria-checkedといった属性が関連することを確認しました。これらの組み合わせは、それぞれのコンポーネントに共通する「パターン」として捉えることができ、1つパターンとして身につけておけば、別のプロジェクトでも再利用しやすくなります。

HTMLのセマンティクスとの関係では、「まずネイティブ要素を使う」「使わなくてよいならARIAを使わない」という原則が大切であることを学びました。<button><a>, <nav>, <main>, <header>といった要素にはもともと意味と暗黙のロールが備わっており、それだけで多くのアクセシビリティが実現できるためです。WAI-ARIAは、<div><span>でカスタムUIを作らざるを得ない場合や、単なるHTMLでは表現しづらい動的な状態を伝える必要がある場合に限定して使うことで、HTMLとARIAの情報が矛盾せず、シンプルで一貫性のある構造を保つことができます。

スクリーンリーダーの視点から見ると、ブラウザが生成するアクセシビリティツリーの情報をもとに、要素のロール・名前・状態を組み合わせて読み上げていることが分かりました。ランドマークロールや見出し、フォームコントロールなどに適切なロールやラベルを指定しておくと、ショートカットキーで効率よくページ内を移動できるようになります。また、aria-liveを使った動的更新の通知や、aria-busyによる処理中表示など、画面の変化を音声で伝える仕組みもWAI-ARIAの重要な活用領域です。

実装ポイントとしては、ロール・名前・状態をセットで設計すること、見た目とARIA属性の状態を常に同期させること、キーボード操作とフォーカス移動を意識すること、フォームのエラー表示や必須項目をARIAで補足すること、そして一度作ったアクセシブルなコンポーネントをパターン化し、チームで共有して再利用することが挙げられました。これらを意識することで、単に仕様をなぞるだけでなく、ユーザー体験全体を見渡した上でのWAI-ARIA活用がしやすくなります。

WAI-ARIAは、覚えるべき属性やルールが多く、最初は難しく感じられるかもしれませんが、「まずHTMLで構造と意味をきちんと表現する」「足りない部分だけARIAで補う」「ロール・名前・状態をそろえる」という流れで考えていくと整理しやすくなります。UIコンポーネントを1つずつ題材にしながら、実際に手を動かして実装と確認を繰り返すことで、WAI-ARIAの知識を現場で活かせるスキルへとつなげていくことができます。

SNSでもご購読できます。

コメントを残す

*