HTMLとARIAから、スクリーンリーダーに何が伝わるか
ブラウザーがHTMLとARIAからアクセシビリティツリーを作る仕組みと、そこに入るロール、名前、説明、状態がそれぞれどこから来るか
要点:ブラウザーはDOMからアクセシビリティツリーを作ります。ページの各部分が、ロール、名前、説明、状態、関係を持つオブジェクトになり、OSのアクセシビリティAPIを通じてスクリーンリーダーに渡ります。HTMLはそれだけでツリーを満たします。ARIAはツリーに入る内容を変えられますが、ページの動作は変えません。
01
使う場面
再現した設定画面について、テスト担当者から3行のメモが届きます。
- ゴミ箱のアイコンは「ボタン」としか読まれない。
- 「メール通知」はただの文字として読まれる。切り替えられることも、オンかどうかも伝わらない。
- 表示名の欄の下にある「プロフィールに表示されます」というヒントが読まれない。
画面ではどれも正しく見え、聞こえなかった言葉もすべてコードに書いてあります。スクリーンリーダーはコードを読むのではなく、ブラウザーから渡された情報を読みます。この記事は、その渡される情報についての話です。
02
DOMからスクリーンリーダーまで
利用者が聞く内容は、4つの層を通って届きます。
- DOM: HTMLとスクリプトが作る要素と属性です。
- アクセシビリティツリー: AccName 1.1の1章は「ユーザーエージェントはDOMから情報を取得し、アクセシブルなオブジェクトからなるアクセシビリティツリーという並行した構造を作る」と説明しています。
- プラットフォームのアクセシビリティAPI: ブラウザーは各オブジェクトをOSのアクセシビリティAPIで公開し、変化があればイベントで知らせます。
- 支援技術: スクリーンリーダー、音声操作、画面拡大ソフトなどがAPIに内容と変化を問い合わせ、音声や点字、コマンドに変えます。
ブラウザーが対応させるAPIは、Core Accessibility API Mappings(Core-AAM)1.2の1.1節に挙げられています。
| プラットフォーム | アクセシビリティAPI |
|---|---|
| Windows | MSAAとIAccessible2、UI Automation |
| macOS | macOS Accessibility Protocol(AX API) |
| Linux | ATKとAT-SPI |
| Android | Androidのアクセシビリティ API |
この一覧にiOSはありません。どのAPIに渡る場合も、ツリーの各オブジェクトが持つ情報の種類は同じです。
| 情報 | 例 | 元になるもの |
|---|---|---|
| ロール | button、checkbox、link | 要素そのもの、または role 属性 |
| 名前 | 「下書きを削除」 | 中身、label、alt、aria-label、aria-labelledby |
| 説明 | 「プロフィールに表示されます」 | aria-describedby、名前に使われていない title |
| 状態とプロパティ(値を含む) | チェック済み、展開、無効、スライダーの値 | 要素自身の状態、または aria-* 属性 |
| 関係 | ラベル付け、制御、所有、親子 | label for、aria-labelledby、aria-controls、aria-owns、入れ子 |
ツリーに届かない内容もあります。WAI-ARIA 1.2は、要素かその祖先が描画されていない(display: none)か、aria-hidden="true" を持つ場合に、その要素を隠れているものとして扱います。AccName 1.2は visibility: hidden も隠れているものに含めています。opacity: 0 の文字や画面外へ移した文字は隠れておらず、ブラウザーはツリーに残します。そのため、aria-hidden のリンクの中のバッジはリンクと一緒にツリーから消えます(画面に見えるのにスクリーンリーダーが読まないテキスト)。一方、画面外へ移して aria-hidden で隠したリンクには、フォーカスが移ってしまいます(Tabで届くのに読み上げられない、aria-hiddenの要素)。
03
ロール:HTMLが決めるもの、ARIAが変えるもの
HTMLの要素にはそれぞれ暗黙のロールがあります。role 属性がないときに対応するロールで、HTML Accessibility API Mappings(HTML-AAM)の3.5節が定めています。
| 要素 | 暗黙のロール |
|---|---|
<button> |
button |
<a href="…"> |
link |
href のない <a> |
generic |
<input type="checkbox"> |
checkbox(チェックの状態も伝わる) |
<nav> |
navigation |
<section> |
アクセシブル名があればregion、なければgeneric |
<div>、<span> |
generic |
role 属性は暗黙のロールを置き換えます。WAI-ARIA 1.2の1.1節によれば、ARIAのロールを付けた要素は、HTMLから受け継ぐ意味にかかわらず、そのウィジェットや構造として扱われます。WAI-ARIA 1.2の5.3節は、ロールを6つに分類しています。
| 分類 | ロールの例 | 作成者にとっての意味 |
|---|---|---|
| 抽象ロール | command、composite、input、landmark、range、roletype、section、sectionhead、select、structure、widget、window | 「作成者は抽象ロールをコンテンツに使ってはならない(MUST NOT)」。体系を作るためだけにある |
| ウィジェット | button、checkbox、option、slider、switch、tab、textbox。複合ウィジェットのcombobox、grid、listbox、menu、radiogroup、tablist、tree | 操作できる部品。キー操作と動作はページが用意する |
| 文書構造 | article、generic、heading、img、list、none、table、toolbar、tooltip | 内容を整理する。通常は操作しない |
| ランドマーク | banner、complementary、contentinfo、form、main、navigation、region、search | 移動の目印になる領域 |
| ライブリージョン | alert、log、marquee、status、timer | 変化が読み上げられる内容 |
| ウィンドウ | alertdialog、dialog | ページの中のウィンドウ |
aria-* 属性は、ステートとプロパティに分かれます。WAI-ARIA 1.2の6.1節によれば、aria-labelledby のようなプロパティは、aria-checked のように操作でよく変わるステートより、値が変わりにくいものです。ただし、これは決まりではなく、aria-valuetext のように頻繁に変わるプロパティもあるとしています。仕様書では、両方をまとめて「属性」と呼ぶことがほとんどです。
どれも、要素の動作は変えません。WAI-ARIA 1.2の1.3節は、ARIAはアクセシビリティAPIに公開する内容を改善するだけで、それ以外はユーザーエージェントは本来どおりに動作すると説明しています。たとえば次のコードです。
<div role="button">変更を保存</div>ツリーには「変更を保存」という名前のボタンが入ります。それでも div にはフォーカスが移らず、EnterキーもSpaceキーも効きません。フォーカスとキー操作は、要素と tabindex、スクリプトが決めるもので、ARIAが決めるものではないためです。続きは「Tabキーで到達できないdivのボタン」で説明しています。
04
ARIAを使うときのルール
W3CのUsing ARIAは、HTMLでARIAを使うときのルールをまとめた文書です。最後の草案(2018年9月27日)にはルールが5つありました。2026年2月24日、W3Cはこの文書を作業中止の草案として公開しました。ルールは「経緯を残し、参照しやすくするため」4つが残され、それ以上の指針はARIA Authoring Practices Guideを参照するよう書かれています。もともと参考情報で、要件ではありません。
| ルール | 内容 | 例 |
|---|---|---|
| 1 | 必要な意味と動作を備えたHTML標準の要素や属性があれば、別の要素にARIAを足さずにそれを使う | Tabキーで到達できないdivのボタン |
| 2 | どうしても必要な場合以外は、HTML標準の意味を変えない。<h2 role="tab"> ではなく <div role="tab"><h2>…</h2></div> と書く |
Using ARIA自身の例。ただしWAI-ARIA 1.2ではtabの子孫は表示のためだけのもの(5.2.9節)で、ブラウザーはh2を見出しとして公開しないことになっている |
| 3 | ARIAで作った操作部品は、すべてキーボードで使えるようにする | ウィジェットの中の矢印キー:roving tabindexとaria-activedescendant |
| 4 | フォーカス可能な要素に role="presentation" や aria-hidden="true" を使わない |
Tabで届くのに読み上げられない、aria-hiddenの要素 |
| 5(2018年の草案のみ) | 操作できる要素には、すべてアクセシブル名を付ける | 名前がないアイコンだけのボタン |
5つ目のルールは作業中止の版にはありません。ただし、求められていること自体は変わらず、WCAG 2.2の達成基準4.1.2が、すべてのユーザーインターフェイスコンポーネントに名前を求めています。
05
名前と説明はどこから来るか
ブラウザーは、Accessible Name and Description Computation(AccName)に従ってアクセシブル名を決めます。W3C勧告は1.1(2018年12月18日)で、1.2はこの記事の日付の時点で草案(2026年10月21日)です。どちらも次の順に調べ、最初に名前が得られたところで決まります(AccName 1.1の4.3節。ラベルの中に埋め込まれたコントロールの手順は省いています)。
- 隠れた要素: 名前になりません。
aria-labelledby、aria-describedby、labelから直接指されている場合は別です。 aria-labelledby: 指している要素の文字を、書いた順につなげます。ページにないidは飛ばされます。「読み上げを誤らせる、無効なARIA」はこの例です。aria-label: その値です。要素の中の文字に代わって名前になります。ボタンに「かごに入れる」と表示しながら別の名前にできてしまうのはこのためで、「表示と名前が食い違うボタンとリンク」で説明しています。- 要素自身のラベル:
label要素、alt、表のcaptionなどです。 - 中身: button、link、checkbox、option、tabなど、中身から名前を得るロールでは、中の文字を子要素ごとに、CSSの
::beforeと::afterの文字も含めて集めます。titleのないSVGしかないボタンでは何も得られません(名前がないアイコンだけのボタン)。 - ツールチップ:
titleです。ほかのどれからも名前が得られなかったときだけ使います。
HTML-AAMの4.1節は、要素ごとにこの中の順番を定めています。テキストの入力欄では、aria-labelledby か aria-label、label、title、placeholder の順です。button では、aria-labelledby か aria-label、label、中身、title の順です。img では、aria-labelledby か aria-label、alt、title の順です。
名前を付けられないロールもあります。WAI-ARIA 1.2の5.2.8.6節にgenericやparagraphなどが挙げられ、AccName 1.2は、作成者がそれらに aria-label や aria-labelledby を使ってはならない(MUST NOT)としています。普通の span に付けた aria-label がスクリーンリーダーに届かないことがあるのはこのためで、「読み上げられないことが多い、divやspanのaria-label」で説明しています。
アクセシブルな説明は、名前とは別のプロパティです。AccName 1.1の4.2節では、aria-describedby が指す要素から作ります。草案のAccName 1.2とHTML-AAMの4.2節は、もう少し詳しい順番を示しています。aria-describedby、aria-description、名前に使われていない表の caption などの要素、名前に使われていない title の順です。aria-description はWAI-ARIA 1.2にはありません。説明をいつ読むかはスクリーンリーダーごとの対応の問題で、ツリーの問題ではありません。組み合わせごとの確認はスクリーンリーダーの対応状況にまとめています。名前を付ける属性の違いは、名前と説明の比較にまとめています。
06
開発者ツールでツリーを読む
次の2つのブラウザーでは、ページから作ったアクセシビリティツリーを見られます。Firefoxのインスペクターが示すのはFirefox自身のツリーで、どのプラットフォームでも同じです。プラットフォームのAPIに渡った形ではありません。
- Chrome: Elementsパネルで要素を選び、Accessibilityタブを開きます(ボタン名などは英語版の表示です。タブはMore Tabsボタンの奥にあることもあります)。ツリーの中での位置、ARIA Attributes、Computed Propertiesが表示されます。Show accessibility treeをオンにすると、DOMツリーの代わりにページ全体のアクセシビリティツリーが表示されます。
- Firefox: 開発ツールのAccessibilityタブを開くか、ページを右クリックしてInspect Accessibility Propertiesを選びます(英語版の表示です)。左のパネルに各オブジェクトのロールと名前が並び、右のパネルに名前、ロール、操作、値、説明、状態、関係などが表示されます。Check for issuesでは、Contrast、Keyboard、Text Labelsの問題だけに絞り込めます。Show Tabbing Orderをオンにすると、フォーカス可能な要素に番号が付きます。
ロールの呼び方はブラウザーによって違い、Firefoxではボタンが「pushbutton」と表示されます。コントロールごとにロール、名前、説明、状態を読み、画面の表示と合っているか確かめます。ツリーで分かるのは情報があるかどうかまでです。スクリーンリーダーがそれをどう読むかは、製品、モード、バージョンによります。ほかの確認方法は「無料のアクセシビリティツール」にあります。
07
実例
テスト担当者のメモにあった設定画面は、次のように作られていました。
<div class="toggle on" onclick="toggleAlerts()">メール通知</div>
<button class="icon-button" onclick="deleteDraft()">
<svg class="icon">
<use href="#bin"></use>
</svg>
</button>
<input id="display-name" placeholder="表示名">
<p class="hint">プロフィールに表示されます</p>開発者ツールで各コントロールを読み、直した結果です。
| コントロール | 修正前のツリー | 修正後のツリー |
|---|---|---|
| メール通知 | 名前を付けられないgenericの div。言葉はただの文字で、状態もフォーカスもない |
「メール通知」という名前の、チェック済みのチェックボックス |
| ゴミ箱アイコン | 名前のないボタン。SVGにtitleがなく、中身から名前が得られない | 「下書きを削除」という名前のボタン |
| 表示名 | placeholderから「表示名」という名前が付いた入力欄。説明はない | ラベルから「表示名」という名前が付き、「プロフィールに表示されます」という説明を持つ入力欄 |
修正後のコードです。
<input type="checkbox" id="alerts" checked>
<label for="alerts">メール通知</label>
<button type="button" aria-label="下書きを削除" onclick="deleteDraft()">
<svg class="icon" aria-hidden="true">
<use href="#bin"></use>
</svg>
</button>
<label for="display-name">表示名</label>
<input id="display-name" aria-describedby="display-name-hint">
<p id="display-name-hint">プロフィールに表示されます</p>切り替えはHTML標準のチェックボックスにしたので、ロールも状態もキー操作もブラウザーが用意します(ルール1)。ゴミ箱のボタンには名前を付け、アイコンはツリーから外しました。入力欄には見えるラベルを付けたので、それが名前になり、入力を始めると画面から消えるplaceholderに頼らなくなりました。ヒントは説明になりました。もう一度ツリーを読むと、どのコントロールにも、テスト担当者が期待したロール、名前、状態、説明があります。その後、実際にどう聞こえるかをスクリーンリーダーで確かめます。
08
よくある間違い
- ロールを付けて終わりにする。
role="button"でツリーにはボタンが入りますが、ARIAが変えるのはツリーだけなので、Tabキーでも他のキーでも操作できないままです。「Tabキーで到達できないdivのボタン」を参照してください。 divやspanにaria-labelで名前を付ける。 genericは名前を付けられないロールなので、その言葉がスクリーンリーダーに届かないことがあります。「読み上げられないことが多い、divやspanのaria-label」を参照してください。- フォーカスが届く要素に
aria-hidden="true"を付ける。 ツリーからは消えてもTabの移動先には残るため、スクリーンリーダーが名前を伝えられない場所にフォーカスが移ります。「Tabで届くのに読み上げられない、aria-hiddenの要素」を参照してください。 - ページにないidを
aria-labelledbyで指す、またはaria-roleと書く。 ブラウザーはたどれない参照も知らない属性も無視しますが、コードは正しく見えたままです。「読み上げを誤らせる、無効なARIA」を参照してください。 - 見えている言葉を含まない
aria-labelを付ける。 名前を決める順番で中身より先に使われるため、ボタンに表示された言葉が名前から消えます。「表示と名前が食い違うボタンとリンク」を参照してください。 - 文字のないアイコンだけにする。 ボタンでは
titleの前に残る手がかりは中身だけですが、titleのないSVGからは何も得られません。「名前がないアイコンだけのボタン」を参照してください。 - 隠した要素の中に情報を置く。
aria-hidden="true"の中にあるものは、すべて一緒にツリーから消えます。「画面に見えるのにスクリーンリーダーが読まないテキスト」を参照してください。 role="widget"のような抽象ロールを使う。 WAI-ARIAが禁じており、HTML-AAMの3.5.1節も、ブラウザーは抽象ロールや無効なロールを無視しなければならない(MUST)としています。使えるロールはHTMLとARIAのリファレンスを参照してください。titleやplaceholderを名前の代わりにする。 HTML-AAMがこれらを使うのは、ほかに名前がないときだけです。placeholderは入力を始めると画面から消えます。「ラベルがない・表示と食い違うフォームの入力欄」を参照してください。
09
規格の定め
- WCAG 2.2 達成基準4.1.2「名前 (name) ・役割 (role) 及び値 (value)」(レベルA、要件): フォーム要素、リンク、スクリプトが生成するコンポーネントを含むすべてのユーザーインターフェイスのコンポーネントについて、名前と役割をプログラムで解釈できること、利用者が設定できる状態、プロパティ、値をプログラムで設定できること、それらの変化の通知が支援技術を含むユーザーエージェントに提供されることを求めています。EN 301 549 V3.2.1では9.4.1.2として同じ要件があります。WCAG 2.0と同じ内容のJIS X 8341-3:2016では4.1.2です。Section 508(E205.4)はWCAG 2.0のレベルAとAAを求めているので、これも含まれます。
- WAI-ARIA 1.2(W3C勧告、2023年6月6日)、ARIAを使うコンテンツへの要件: 「作成者は抽象ロールをコンテンツに使ってはならない(MUST NOT)」(5.3.1節)。
aria-hiddenについては、見えている内容をスクリーンリーダーから隠す場合、同じか同等の意味と機能を支援技術に公開しなければならない(MUST)としています。1.3節と6.1節は仕組みの説明で、要件ではありません。 - AccName 1.1(W3C勧告、2018年12月18日)、ブラウザーへの要件: その手順でアクセシブル名を計算しなければならない(MUST)としています(4.1節)。AccName 1.2は草案です。新しい版が出たかどうかは、W3Cの公開履歴で確かめられます。
- HTML-AAM 1.0(草案、2026年10月26日)とCore-AAM 1.2(勧告候補草案、2026年10月30日): ブラウザーが何をどう対応させるかを定めるもので、作成者が書く内容を定めるものではありません。Core-AAMの最新の勧告は1.1です。
- Using ARIA(作業中止の草案、2026年2月24日): 助言だけで、今後は更新されません。
10
制限
この記事が扱うのは、ツリーに何が届くかまでです。次のことは扱いません。
- スクリーンリーダーごとの読み方とモード: 組み合わせごとに確かめる対応の問題です。EaseWebの結果はスクリーンリーダーの対応状況にあります。
- ウィジェットの中のキー操作: ウィジェットの中の矢印キー:roving tabindexとaria-activedescendantを参照してください。
- 要素、ロール、属性それぞれの働き: HTMLとARIAのリファレンスを参照してください。
- ブラウザーとバージョンによる違い: ツリーは製品ごとに違い、上の対応にはまだ草案の部分もあります。ツリーを読んだブラウザーとバージョンを記録してください。
コントロールの名前や説明が利用者にとって適切かどうかは、実際のページを見てテスト担当者が判断します。
11
用語
- アクセシビリティツリー
- ブラウザーがDOMから支援技術のために作る、アクセシブルなオブジェクトの木構造。各オブジェクトがロール、名前、状態、関係を持つ。
- アクセシビリティAPI
- アプリケーションの各部分を支援技術が読み取り、操作するためにOSが用意するインターフェイス。WindowsのUI Automationなど。
- アクセシブル名
- アクセシビリティツリーでオブジェクトを特定する短い文字列。ボタンのラベルなど。
- アクセシブルな説明
- 名前とは別に、オブジェクトについて補足する文字列。入力欄の下のヒントなど。
- 暗黙のロール
- role属性がないときにHTML要素が持つロール。HTML-AAMが対応を定めている。button要素はbutton、nav要素はnavigation。
- 抽象ロール
- WAI-ARIAがロールの体系を作るためだけに使うロール。widgetやinputなど。作成者はコンテンツに使ってはならない。
12
出典とガイド
- 参考 WAI-ARIA 1.2: 1.3 ユーザーエージェントの対応(新しいタブで開きます)
- 規定 WAI-ARIA 1.2: 5.3 ロールの分類(新しいタブで開きます)
- 規定 WAI-ARIA 1.2: 6.1 ステートとプロパティの違い(新しいタブで開きます)
- 規定 Accessible Name and Description Computation 1.1: 4.3 名前と説明の計算(新しいタブで開きます)
- 参考 Accessible Name and Description Computation 1.2(草案): 4.2 説明の計算(新しいタブで開きます)
- 参考 HTML Accessibility API Mappings 1.0(草案): 3.5 HTML要素とロールの対応(新しいタブで開きます)
- 参考 HTML Accessibility API Mappings 1.0(草案): 4.1 HTML要素ごとの名前の計算(新しいタブで開きます)
- 参考 Core Accessibility API Mappings 1.2(勧告候補草案): 1.1 アクセシビリティAPI(新しいタブで開きます)
- 参考 Using ARIA(作業中止): 2. HTMLでARIAを使うときの注意(新しいタブで開きます)
- 規定 WCAG 2.2: 4.1.2 名前 (name) ・役割 (role) 及び値 (value)(新しいタブで開きます)
関連するガイド
- Tabキーで到達できないdivのボタン クリックでしか操作できないため、キーボードでは保存できない
- 名前がないアイコンだけのボタン ヘッダーの検索、かご、メニューのボタンはSVGアイコンだけで言葉がないため、どれも「ボタン」としか読み上げられない
- 表示と名前が食い違うボタンとリンク ショップが「かごに入れる」ボタンそれぞれに商品名と「カート」を含む aria-label を付けたため、名前にボタン上の言葉が含まれず、音声操作でボタンを見つけられない
- 読み上げを誤らせる、無効なARIA 購入手続きの独自のギフト包装チェックボックスが、書き換える前のラベルのidを aria-labelledby で指していたり、ロールを aria-role で書いていたりするため、スクリーンリーダーには名前のないボックスか、チェックボックスですらないものとして伝わってしまう
- 読み上げられないことが多い、divやspanのaria-label 商品一覧が星の評価と送料無料のアイコンを aria-label 付きの span に入れているが、ロールのない span には名前を付けられないため、多くのスクリーンリーダーは評価の場所で何も読み上げない
- Tabで届くのに読み上げられない、aria-hiddenの要素 閉じたスライド式メニューを画面の外に移してaria-hidden="true"を付けているため、リンクはスクリーンリーダーからは隠れても、すべてTabの移動先に残ってしまう
- 画面に見えるのにスクリーンリーダーが読まないテキスト 店が「売り切れ」のバッジを商品写真のリンクの中に描き、そのリンクを重複としてスクリーンリーダーから隠しているため、どのマグが売り切れなのかが読み上げられない
この記事は実務の進め方を、再現した例で説明するものです。スクリーンリーダーでのテスト結果ではなく、特定の顧客についての指摘でもありません。