アクセシビリティツリーは画面とは別の文書だ
一言でいうと
ブラウザーは、DOMとは別にアクセシビリティツリー(accessibility tree)を作ります。スクリーンリーダーが読むのは画面ではなくそのツリーであり、ARIAはそのツリーだけを直します。動作は何も変えません。
なぜ必要なのか
アクセシビリティを直すよう指摘されると、人はたいていroleとaria-*を付けます。ところが、その結果が元よりも悪くなることがよくあります。実際に、ARIAを多く使ったページのほうが、使っていないページよりもエラー率が高いという調査が、繰り返し出ています。
理由は単純です。ARIAは約束であって、実装ではありません。role="button"を付けると、支援技術に「これはボタンです」と伝えたことになり、その瞬間から、ボタンのように動作させるのは、すべて開発者の仕事になります。言うだけで実装しなければ、スクリーンリーダーのユーザーは、ボタンだと案内されて押したのに何も起きない画面に出会います。何も言わなかったときよりも悪い状態です。
どう動くのか
DOMからアクセシビリティツリーが作られるとき、要素ごとに4つのことが決まります。
| 項目 | 何か | 例 |
|---|---|---|
| role | これは何か | button・link・heading・list |
| name | 何と呼ぶのか | 「保存」 |
| state | 今どんな状態か | 押されている・展開している・無効 |
| value | どんな値か | スライダーの40 |
ネイティブのHTML要素は、この4つを最初から持って生まれます。<button>はroleがbuttonで、中の文字がnameになり、disabledがstateになり、フォーカスとEnterキー・Spaceキーの処理がブラウザーに組み込まれています。
<div role="button">が得るのは、このうちroleだけです。残りの3つと動作は、すべて自分で作る必要があります。MDNのアクセシビリティツリーの説明とARIA in HTMLが、この関係を整理しています。
5つのルール
WAI-ARIAの使用ルールは、5行に要約されます。順番に重要です。
- 使えるHTML要素があれば、それを使います: ARIAは、対応する要素がないときだけ使います
- ネイティブの意味を変えません:
<h2 role="tab">は、見出しの一覧からその項目を消します - すべてのインタラクティブなARIAウィジェットは、キーボードで操作できなければなりません
- フォーカスを受け取れる要素に、
role="presentation"やaria-hidden="true"を使いません - すべてのインタラクティブな要素には、アクセシブルネームが必要です
2つ目のルールが、最も静かに事故を起こします。roleは付け足すものではなく、上書きするものです。<ul role="presentation">を使うと、そのリストはもうリストではなくなり、スクリーンリーダーは「5項目」と伝えてくれません。デザイン上の点(ブレット)を消そうとして付けたのに、構造をまるごと消してしまうことが、実際によくあります。
状態は嘘をつきやすい
aria-expanded、aria-selected、aria-checked、aria-currentのような状態の属性は、自分で更新する必要があります。矢印アイコンを回転させるCSSは自動的に更新されますが、これらの属性はそうではありません。
そのため、よく起きる事故が次のものです。パネルは開いたのに、aria-expandedは相変わらずfalseのままです。目で見る人にとっては開いた画面であり、スクリーンリーダーのユーザーにとっては閉じた画面です。どちらかが嘘をついていて、嘘をつくのは、いつも支援技術の側です。
aria-controls、aria-labelledby、aria-describedbyは、idで別の要素を指します。そのidがなくても、ブラウザーはエラーを出さずに黙って無視します。コンポーネントを移動したり、条件付きでレンダリングしたりしているうちに参照が切れることはよくありますが、画面には何の変化もないため、誰も気づきません。
ライブリージョン
検索結果の件数、保存完了の案内、エラーメッセージ。あとから現れる文字は、それだけでは読み上げられません。スクリーンリーダーは、すでにほかの場所を読み上げているからです。
role="alert": 今すぐ読み上げます。エラーに使いますaria-live="polite": 今読んでいるものが終わったら読み上げます。結果の件数のようなものに使いますrole="status": politeと同じ意味のロールです
注意点が1つあります。ライブリージョンは、あらかじめDOMに存在している必要があります。要素をまるごと新しく作りながらrole="alert"を付けても、読み上げられない場合が多くあります。空のコンテナーを先に置いておき、その中の文字を変えるのが安全です。
現場での姿
3つのことが、繰り返し出てきます。
1つ目は、アイコンボタンに名前がないことです。<button><svg/></button>は名前がないため、「ボタン」とだけ読み上げられます。aria-label="닫기"(韓国語で「閉じる」を意味する語です)の1行で解決するのに、最も頻繁に抜けています。
2つ目は、装飾アイコンを隠していないことです。文字で描いたアイコン(★、▶)がそのまま読み上げられ、「黒い星 お気に入り」のような音声になります。aria-hidden="true"を付けると、静かになります。
3つ目は、デザインシステムがroleを乱発することです。一度誤って使ったroleが、コンポーネントを通じてサイト全体に広がります。これは、個別のページを直しても防げず、コンポーネント単位で押さえる必要があります。
覚えておくことは1つです。悪いARIAは、ないほうがましです。直す前に最初に問うべき質問は、「どのroleを付けようか」ではなく、これをするHTML要素がすでにないかという問いです。
次のクイズで確認すること
アクセシビリティツリーが何を持つのか、roleが何を上書きするのか、状態の属性とライブリージョンがどのように静かに壊れるのかを確認します。