はじめに: aria-labelを書いたあと、何が起こっている?
次のコードから考えてみます。
<button aria-label="保存">
💾
</button>
一般的なアクセシビリティ解説では、「aria-labelに『保存』と設定したので、支援技術にボタンの名前を伝えられる」と説明されることがあります。
では、なぜHTML属性であるaria-labelを、スクリーンリーダーが知ることができるのでしょうか。スクリーンリーダーがHTMLソースをそのまま読んでいるわけではありません。
HTML
↓
???
↓
スクリーンリーダー
この「???」にあるレイヤーを追うのが、この記事の目的です。WebアクセシビリティをARIA属性の書き方だけではなく、UIの意味がユーザーまで届く情報伝達経路として見ていきます。
HTMLからDOMへ
ブラウザーはHTMLを解析し、要素・属性・テキストをDOM(Document Object Model)として扱います(WHATWG HTML Standard)。パース処理の詳細はここでは扱いません。ReactなどのUIフレームワークを使っていても、ブラウザーが扱う段階ではDOMが土台になります。
ただし、DOMそのものが支援技術へ渡る最終的なUI表現ではありません。DOMには画面の構造やスクリプトが扱う情報が含まれますが、支援技術が知りたいのは、たとえば「これは何のUIか」「名前は何か」「操作できるか」「現在どんな状態か」です。
この意味を公開するために、ブラウザーはDOMの情報をAccessibility Treeへつなぎます。
DOMからAccessibility Treeへ
ブラウザーはDOMやHTMLの意味、ARIA属性などをもとに、Accessibility Tree(アクセシビリティツリー)を構成します。これは支援技術へ公開する意味を理解するためのモデルです。WAI-ARIAはARIAのロール・状態・プロパティの意味を定義し、Accessible Name and Description Computationは名前や説明の計算方法を定めています。仕様がブラウザー内部に同一のツリー実装を要求している、ということではありません。Chrome DevToolsで見えるツリーは、Chromeが計算・公開する情報を観察するためのものです。
Accessibility Tree上の情報は、次のように考えられます。
| 情報 | 何を表すか | 例 |
|---|---|---|
| Role(役割) | どのようなUIか | button、link |
| Name(名前) | そのUIを何と呼ぶか | 保存、検索 |
| Description(説明) | 名前を補う情報 | 入力形式や追加の案内 |
| State / Properties(状態・プロパティ) | 現在の状態や関係 | 無効、展開中、チェック済み |
例のHTMLを、概念的に表すと次のようになります。
role: button
name: 保存
<button>にはHTMLにもともと「ボタン」という意味があり、aria-labelが「保存」というアクセシブルネームを与えます。この例では、ボタン名の計算にaria-labelが使われるため、絵文字の読み方ではなく操作の目的を名前として公開できます。名前の計算にはaria-labelledbyなど別の命名方法との優先関係もありますが、ここではほかの命名方法がないものとします。
理解のために単純化すると、次の対比になります。
DOM
→ UIがどのように実装されているか
Accessibility Tree
→ UIがどのような意味として公開されているか
ただし、これは役割の違いをつかむための単純化です。DOMとAccessibility Treeが一対一で変換されるわけではありません。たとえば、DOM上にあってもアクセシビリティ上不要なノードは公開されないことがあります。一方、CSSで生成されたコンテンツなど、DOMに直接対応する要素がない情報がツリーに現れることもあります。Chromeの実装資料も、DOMとアクセシビリティツリーが一対一ではない例を挙げています。
そのため、アクセシビリティ対応の目的はARIA属性を書くこと自体ではありません。意図した名前・役割・状態などの意味が、支援技術へ適切に公開されるよう設計することです。
Chrome DevToolsで3つの例を比べる
ここで、説明だけで終わらないように、3つの要素を同じページに置いてChrome DevToolsで比べてみます。
<main>
<button id="native-button">保存</button>
<div id="plain-div">保存</div>
<button id="named-button" aria-label="ファイルを保存">
💾
</button>
</main>
Chrome DevToolsの現行ドキュメントに沿って確認するには、Elementsパネルで対象要素を選び、Accessibilityタブを開きます。タブが見つからない場合はMore Tabsにあります。各要素のARIA属性やComputed Propertiesを見た後、同じタブでShow accessibility treeを有効にすると、ページ全体のツリーを確認できます。
同じ3要素をChromeで観察すると、次のような違いが見えます。
| パターン | DOM上の要素 | Chromeが公開するrole / name | Tabキーでフォーカス |
|---|---|---|---|
| 1 |
buttonとテキスト「保存」 |
button / 保存 | できる |
| 2 |
divとテキスト「保存」 |
generic / 要素自体の名前なし。子テキストは別に見える | 通常はできない |
| 3 |
buttonとaria-label、絵文字 |
button / ファイルを保存 | できる |
パターン1では、<button>のネイティブな意味からroleと名前が得られます。ARIAを追加しなくても、ボタンとしてTabキーでフォーカスできます。
パターン2は、見た目に文字が表示されていても、ボタンの役割やキーボード操作はありません。今回Chromeで確認したツリーでは、divはgenericとして現れ、テキストは子のノードとして確認できました。つまり、文字が読めることと、操作要素として意味が伝わることは別です。
パターン3では、ネイティブボタンのroleとキーボード操作はそのままに、アクセシブルネームがaria-labelの「ファイルを保存」になります。子の絵文字がボタンの名前になるのではなく、指定した名前が公開されます。
表のTabキーの列は、Accessibility Treeのroleやnameだけから判断するものではありません。実際にページでTabキーを押してフォーカス位置を確かめます。HTML要素の既定のフォーカス動作については、MDNのtabindex解説も参照してください。divでもtabindexを追加すればフォーカス可能にはできますが、それだけでボタンのroleやキーボード操作が備わるわけではありません。
Accessibility Treeはゴールではない
ここまで、次の経路を追ってきました。
HTML
↓
DOM
↓
Accessibility Tree
しかし、支援技術が利用できる情報になるまでには、その先のレイヤーがあります。
HTML
↓
DOM
↓
Accessibility Tree
↓
Platform Accessibility API
↓
支援技術
↓
ユーザー
WindowsやmacOSなどでは、それぞれ異なるAccessibility APIが使われます。たとえばWindowsのUI Automation、macOSのAccessibility API、LinuxのATK/AT-SPIは別々のインターフェースです。W3CのHTML-AAMとCore-AAMは、HTMLやARIAが表す意味を、各プラットフォームのAPIにどう対応づけるかを扱っています。
たとえばWindowsでは、UI Automation(UIA)がUI要素をツリーとして公開し、要素のコントロール種別やNameなどの情報をクライアントから取得できるようにします。Microsoft Edgeの資料は、EdgeがARIAのロール・状態・プロパティを認識し、支援技術がUI Automation APIを使ってそれらを取得できるよう公開すると説明しています。
したがって、次のような単純な構造ではありません。
ARIAを書く → スクリーンリーダーがARIAを直接読む
また、「スクリーンリーダーはAccessibility Treeを直接読む」とも限りません。Accessibility Treeはブラウザー内部の意味表現として考えると分かりやすい一方、支援技術との接点は、環境ごとのPlatform Accessibility APIです。内部のデータ構造や連携方法は、ブラウザー・OSの実装によって異なります。
DevToolsで正しければアクセシブルなのか?
答えは「それだけでは十分ではない」です。Accessibility Treeの確認で分かるのは、情報伝達経路のうち、ブラウザーがUIの意味をどう公開しているかです。利用者が実際に操作できるか、目的を理解できるかは、それだけでは確かめきれません。
DOMを見る
↓
実装を確認する
Accessibility Treeを見る
↓
ブラウザーがUIの意味をどう公開しているか確認する
キーボードやスクリーンリーダーで操作する
↓
実際の操作体験を確認する
スクリーンリーダーはAPIから得た名前・役割・状態を使いますが、読み上げる順序や言い回しは常に同一ではありません。W3CのAccessible Name and Description Computationも、役割と名前の提示方法などはプラットフォームAPIや支援技術によって決まると説明しています。実際の読み上げや操作を確認するには、対象とするブラウザーとスクリーンリーダーの組み合わせで試す必要があります。
つまり、「自動テストを通過したからアクセシブル」「Accessibility Treeが正しいからアクセシブル」とは言えません。DOM、ブラウザーが公開する意味、キーボード操作、支援技術を使った体験を、それぞれの段階で確かめることが大切です。
ReactやUIフレームワークを使うと、この経路はどうなる?
Reactでは、コンポーネントの中にaria-labelを書きます。しかしReactがスクリーンリーダーへラベルを直接送るわけではありません。Reactのドキュメントでも、aria-*属性は組み込みのDOMコンポーネントで使われ、属性名はHTMLと同じと説明されています。レンダリング結果のDOMをブラウザーが解釈し、その先のAccessibility TreeやPlatform Accessibility APIへつなぎます。
React Component
↓
UI Component Library(Fluent UIなど)
↓
DOM
↓
Accessibility Tree
↓
Platform Accessibility API
↓
JAWS / NVDA / Narrator など
↓
ユーザー
UIフレームワークやコンポーネントライブラリは、適切なHTMLやARIAを出力する助けになります。ただし、それだけでユーザーまで意味が正しく届く保証にはなりません。レンダリング後のDOMを確認し、ブラウザーが計算した情報と、実際の支援技術での操作を確かめるという流れは変わりません。
おわりに: アクセシビリティを「情報の経路」として考える
aria-labelは、スクリーンリーダーへの音声命令ではありません。HTML上の情報がDOMに入り、ブラウザーが役割や名前などの意味を計算し、Accessibility TreeやPlatform Accessibility APIを通じて支援技術へ届きます。支援技術はその情報を、環境やユーザーの操作に応じた形で提示します。
HTML / UI Framework
↓
DOM
↓
Accessibility Tree
↓
Accessibility API
↓
支援技術
↓
ユーザー
ARIA属性のテクニックだけを見るのではなく、途中の各レイヤーで意味が保たれているかを確かめる。アクセシビリティを情報伝達の経路として捉えることで、「なぜARIAが効くのか」だけでなく、「どこで意図が失われるのか」も考えやすくなります。
次の記事では、今回の経路にShadow DOMが加わると何が起きるのかを考えます。Shadow DOMの向こう側は支援技術からどう見えるのでしょうか?
私たちが作っているのはDOMそのものではありません。その先にいるユーザーへ届くUIを作っています。
参考資料
- WHATWG HTML Standard: Parsing HTML documents
- MDN: Accessibility tree
- W3C: Accessible Rich Internet Applications (WAI-ARIA) 1.2
- W3C: Accessible Name and Description Computation 1.2
- W3C: HTML Accessibility API Mappings 1.0
- W3C: Core Accessibility API Mappings 1.2
- Chrome for Developers: Accessibility features reference
- Chromium: How Chrome Accessibility Works(Chromeの実装資料)
- Microsoft Edge: ARIA and UI automation
- Microsoft Learn: UI Automation Overview
- MDN:
tabindexglobal attribute - React: Common components