トピックを検索
読み上げを誤らせる、無効なARIA
購入手続きの独自のギフト包装チェックボックスが、状態を "yes" と "no" で書いたり、ロールを aria-role で書いたりしています。どちらもARIAにない言葉のため、スクリーンリーダーには状態が変わらないボックスか、チェックボックスですらないものとして伝わります。
aria-checked="no" 問題
スクリーンリーダー利用者が、ギフト包装を選んでいるかどうかも、ボックスがチェックできるものかどうかも分かりません。
ショップの購入手続きの画面で、「注文を確定する」ボタンの上に選択肢が一つあります。小さな四角いボックスと「ギフト包装にする」という言葉です。これは本物のチェックボックスではありません。ボックスとして描いた div に、クリックやSpaceキーでチェックマークを描くスクリプトと、支援技術に何であるかを伝えるARIAが付いています。そのARIAに、間違った言葉が一つ入っています。あるバージョンでは、スクリプトが状態を aria-checked="yes" と aria-checked="no" で書きます。スクリーンリーダー利用者がTabキーでボックスに移動すると、「ギフト包装にする、チェックボックス、チェックあり」と聞こえます。ギフト包装は不要なのでSpaceキーを押しますが、チェックありのまま読まれます。ボックスは空だったのに、今はチェックが入っています。別のバージョンでは、ロールが aria-role="checkbox" と書かれています。スクリーンリーダーは「ギフト包装にする」とだけ読み、ロールも状態も伝えず、Spaceキーを押しても何も読みません。
ARIAは決まった語彙です。ロール、属性、属性が取れる値のそれぞれが仕様に定められていて、ブラウザーは知っている言葉だけでアクセシビリティツリーを作ります。それ以外は作成者の誤りで、仕様はその扱い方をブラウザーに定めています。作成者の意図を推し量るわけではありません。
aria-checkedが取る値は"true"、"false"、"mixed"です。オンかオフかとして公開する状態に未知の値があるとき、ARIAは空の値と"undefined"以外をすべて true として扱うようブラウザーに定めています。Chromeはそのとおりに動き、"no"も"yes"と同じくチェックありと読まれます。ボックスの表示に関係なく、常にチェックありです。aria-roleは属性ですらありません。ブラウザーはこれを無視するため、divは自身のロールであるgenericのままで、状態を持ちません。隣の正しいaria-checkedも、generic の要素はチェックできないため無視されます。role="check-box"のように、ARIAが定義するロールを含まないroleは、ロールが指定されていないものとして扱われ、結果は同じです。
どちらのバージョンも、4.1.2(名前・役割・値)を満たしません。ソースではどちらも設定しているように見えても、操作要素が支援技術に公開すべきロールか状態が、誤っているか欠けています。
最初のバージョンは、手早い確認を通ってしまいます。Chromeでスクリーンリーダーを動かしてボックスにチェックを入れると、「チェックあり」と読まれます。問題が分かるのは空のボックスのときだけで、その状態はあまり確かめられません。同じ未知の値を別の扱い方をするブラウザーもあり、同じページで聞こえる内容が人によって違うこともあります。
10グループ中2グループに影響
axe はARIAの言葉を一つずつ仕様と照らし合わせるため、aria-checked="no"(aria-valid-attr-value)、aria-role(aria-valid-attr)、role="check-box"(aria-roles)を確実に報告します。ただし、確認するのは見つけたときの状態のページだけです。最初のクリックの後にだけ "no" を書くスクリプトは、読み込み時のページを調べても見つかりません。正しい言葉のロールが操作要素に合っているか、aria-checked がチェックマークに従っているかも、自動チェックには分かりません。手早い修正が状況を悪くすることもあります。axe が指摘した属性を削除すると、報告は消えますが、ロールのないボックスが残ります。aria-role を role に直しても、スクリプトが "yes" と "no" を書き続けるなら、一つの問題がもう一つに入れ替わるだけです。「念のため」HTML標準のチェックボックスに aria-checked を足すと、ブラウザーが無視する属性が増え、本来の状態と食い違った時点で、axe は aria-conditional-attr として報告します。
試してみる
Tabキーでギフト包装のボックスへ移動し、Spaceキーでチェックを入れたり外したりしてください。
ボックスが空のときとチェックが入っているときで、表示とスクリーンリーダーの読み上げを比べてください。読み上げはチェックマークに従っていますか?
問題あり 修正できますか?
スクリーンリーダーの読み上げ:
(Tabでボックスへ移動)
修正方法
ロール、属性、値をARIAの定義どおりに書くか、HTML標準のチェックボックスを使ってARIAをなくします。
- 1ARIAが定義する言葉だけを使う。
aria-roleではなくrole、"yes"と"no"ではなく"true"と"false"。 - 2ARIA属性は対応するロールにだけ付け、HTML標準の操作要素が持つ状態を重ねて書かない。
- 3読み込み時だけでなく、すべての状態で、表示された後のマークアップを確認する。
状態を "yes" と "no" で書く
<div class="tick-box" role="checkbox" aria-checked="no" tabindex="0"> ギフト包装にする</div>ARIAの定義どおりに書く
<div class="tick-box" role="checkbox" aria-checked="false" tabindex="0"> ギフト包装にする</div>ロールは正しく、チェックマークも描かれます。しかし "yes" も "no" も aria-checked の値ではないため、Chromeはどちらもチェックありと読みます。空のボックスがチェックありと伝わります。
role="checkbox" でボックスにロールが付き、aria-checked は "true" か "false" を持ちます。スクリーンリーダーは「ギフト包装にする、チェックボックス、チェックなし」と読み、Spaceキーかクリックの後は「チェックあり」と読みます。チェックマークは aria-checked そのものから描くため、見える状態と聞こえる状態がずれません。保存したカートのように、ギフト包装がすでに選ばれているときは、ページが aria-checked="true" を出力する必要があります。
どちらも基準を満たします。ARIAを直す方法は今のコンポーネントのまま直せますが、ロール、状態、Spaceキー、保存した状態のすべてを自分で正しく保つ必要があり、今後の編集で無効な言葉がまた入るおそれがあります。HTML標準のチェックボックスは、ARIAもスクリプトもなしに同じ役割を果たすため、書き間違える言葉が残らず、動作はブラウザーが保ちます。こちらを使ってください。ARIAを直す方法は、まだ構造を変えられないコンポーネントのために取っておきます。
テンプレートではなく、表示されたものを確認する。 フレームワークやコンポーネントは、それぞれのやり方で値を属性に変えます。真偽値のpropsが "true" になることも、値のない属性になることも、出力されないこともあり、渡された文字列をそのまま渡すコンポーネントもあります。状態を変えるたびに、ブラウザーの開発者ツールで属性を確認してください。
axe が報告する、ほかの無効なARIA。 同じ問題は別の形でも現れ、それぞれに別のルールがあります。直し方は共通で、その場所にARIAが定義する言葉を使うか、ARIAの要らないHTML標準の要素を使います。
- ARIAが定義していないロールや、
role="input"やrole="widget"のような抽象ロール(aria-roles)。checkboxやnavigationのような実在のロールか、navのようなHTML要素を使う。 - ボタンの
aria-checkedのように、そのロールが対応していない属性(aria-allowed-attr)。押したままになるボタンにはaria-pressedを使う。押された状態が伝わらないトグルボタンを参照。aria-roleのせいでdivに残ったaria-checkedも、このルールで報告される。 <input type="checkbox">のaria-checkedのように、HTML標準の操作要素の状態を重ねて書いたARIA(aria-conditional-attr、本来の状態と食い違うと報告される)。削除する。ブラウザーはどちらにしても本来のcheckedを使う。role="directory"のような非推奨のロール(aria-deprecated-role)。listかulを使う。- ほかに名前のない要素の
aria-braillelabelのように、対応する名前のない点字用のラベル(aria-braille-equivalent、確認対象として送られる)。まず通常の名前を付ける。
一部のブラウザーでは動く書き間違い。 ChromeとSafariは aria-labelledby の代わりに aria-labeledby も受け付けますが、Firefoxは受け付けないため、Chromeではラベルが正しく見えても、Firefoxでは欠けることがあります。axe はこれを aria-valid-attr として報告します。Lを二つ書いてください。
何も指さない参照。 ページにないidを指す aria-labelledby や aria-describedby は、ないものとして無視されます。axe はこれを問題として報告せず、aria-valid-attr-value の確認対象として送ります。属性があるときは常にそのidが存在するようにし、# を付けずに書いてください。
言葉は正しくても使い方が誤っている場合は、別のガイドで扱います。 名前を持てない div や span の名前は読み上げられない、divやspanのaria-label、状態がまったくない場合はオンかオフかが伝わらないトグルスイッチを参照してください。フォーカスを受けられる要素の aria-hidden はTabで届くのに読み上げられない、aria-hiddenの要素、項目のないリストのロールはdivやアイコン、手入力の番号で作った見た目だけのリストで扱っています。
AI コーディングアシスタントで修正しますか? このガイドを Markdown で取得、または全ガイドをコーディングエージェントで使う。
修正の確認
5つの確認
確認方法: スクリーンリーダー、キーボード、マウス
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。コンポーネントや、その状態を保存するコードを変更した際は、再度確認します。
制限
このサンプルは、どのアプリケーションでも同じ結果になることを保証するものではありません。クラス名は仮のもので、チェックボックスは、ARIAを手で書いた独自の操作要素の一例です。作成者の誤りの扱い方はブラウザーごとに違い、バージョンによっても変わるため、一つのブラウザーで正しく聞こえても、確かめられたことはわずかです。言葉が正しいARIAが、使い方も正しいとは限りません。自動チェックで何も報告されなくても、ロールが操作要素に合っているか、状態が画面に従っているかは分かりません。対象とする支援技術で、操作全体を確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
参考資料
- WCAG Understanding: name-role-value(新しいタブで開きます)
- w3.org: wai-aria-1.2(新しいタブで開きます)
- w3.org: wai-aria-1.2(新しいタブで開きます)
- w3.org: using-aria(新しいタブで開きます)
- developer.mozilla.org: checkbox_role(新しいタブで開きます)
- dequeuniversity.com: aria-valid-attr-value(新しいタブで開きます)
- dequeuniversity.com: aria-valid-attr(新しいタブで開きます)
- dequeuniversity.com: aria-roles(新しいタブで開きます)
コードを更新しても、使いやすさを保つために
ガイドの内容を、コーディングエージェントやCIで使える開発ルールにまとめます。更新後は、キーボードとスクリーンリーダーで再テストし、問題が再発していないか確認します。