トピックを検索
キーボードでは展開できない表の行
タスクの表で、サブタスクを開くには行の矢印をクリックするしかないため、キーボードでは開けず、スクリーンリーダーにはその行が開くことも伝わらない
<span class="toggle"></span> 問題
キーボード利用者は表のタスクを開いてサブタスクにたどり着けず、スクリーンリーダー利用者には、どのタスクが開くのか、開いているのか、どの行がサブタスクなのかが伝わらない
あるチームのプロジェクト管理画面では、タスクが「タスク」「担当者」「期限」の3列の表に並び、見出しの行は th のセルで作られています。表そのものの作りに問題はありません。サブタスクのあるタスクには、名前の前に矢印があります。矢印をクリックすると、その下にサブタスクが字下げされて開き、矢印が下を向きます。各タスクの名前は、そのタスクのページへのリンクです。「春のカタログを公開する」は開いていて、「ドメインを更新する」にはサブタスクがなく、「夏のセールを企画する」は閉じています。矢印は、CSSで描いた中身のないspanで、クリック処理が付いています。マウスを使わずにTabキーを押すと、フォーカスはタスクのリンクを、表示中のサブタスクも含めて順にたどり、表を出て「完了したタスク」へ移ります。「夏のセールを企画する」は開けないため、「メールマガジンの枠を押さえる」には届きません。スクリーンリーダーでは、表示中の行が表の行として読まれるだけで、タスクが開くことも、開いているかどうかも、「商品説明を書く」がすぐ上のタスクのサブタスクであることも伝わりません。
表のプラグインDataTablesが公式に載せている、行を開く例も、この作りです。クリック処理は表のセルに付いていて、tabindex もキー操作の処理もありません。そこでチームは、表を手っ取り早くツリーグリッドにします。表に role="treegrid" を付け、ほかには何もしません。キーボード操作は何も変わらず、Tabキーは今もすべての矢印を飛ばします。スクリーンリーダーは表をツリーグリッドと伝えるようになりますが、その中で矢印キーを押しても何も起きず、どの行も自分の階層を伝えず、どのタスクも開いているかどうかを伝えません。
達成基準2.1.1「キーボード」は、マウスでできることはすべてキーボードでもできることを求めています。タスクを開く方法は、フォーカスを受けないspanのクリックだけで、ブラウザーがEnterキーとSpaceキーをクリックに変えるのはHTML標準のコントロールだけです。閉じたタスクのサブタスクには、マウスなしでは届きません。role="treegrid" を足しても、ここは何も変わりません。矢印は今もフォーカスを受けず、ツリーグリッドなら使えるはずの矢印キーを押しても何も起きません。
達成基準4.1.2「名前(name)・役割(role)及び値(value)」は、どのコントロールも名前、役割、状態をプログラムで判別できることを求めています。矢印は、クリックするとタスクが開くコントロールです。中身のないspanのため、役割も名前も、展開と折りたたみの状態もありません。スクリーンリーダーでは、タスクが開くことも、開いたタスクと閉じたタスクの違いも分かりません。W3Cはこれを失敗例として挙げています。スクリプトでspanやdivをコントロールにしながら役割を与えないものが失敗例F59、より広く、アクセシビリティAPIを使わないか一部しか使わない独自のコントロールが失敗例F15です。
達成基準1.3.1「情報及び関係性」は、画面上の構造がコードにもあることを求めています。字下げは、「商品説明を書く」が「春のカタログを公開する」に属することを示しています。表では、そのことがマークアップのどこにも書かれていません。どの行もほかの行と同じ行で、入れ子は余白でしかありません。ツリーグリッドの行には入れ子にするグループの要素がないため、階層を伝えるのは aria-level だけです。aria-level がなければ、ロールを付けても行が並ぶだけのグリッドになり、サブタスクがどのタスクに属するかは伝わらないままです。
行が開く表は、プロジェクトやタスクの管理画面、注文が配送ごとに開く注文履歴、費目が明細に開く予算表、フォルダーがファイルに開くファイル一覧などにあります。下の行に並ぶサブタスクや配送、明細には、たいていその行からしかたどり着けません。開けない行があると、クリックできない人はその中身に届きません。
10グループ中2グループに影響
最初の表には、axeが確認するものがありません。クリック処理を付けたspanには役割がないため、名前や状態を求めるルールは当てはまらず、表の見出しにも問題はありません。同じ表に role="treegrid" を付けたものも、axeのWCAGのルールに合格します。どの行もツリーグリッドの中にあり、どのセルも行の中にあるからです。axeが確かめるのはロールの親子関係(表やグリッドの外にある row は aria-required-parent、行のない treegrid は aria-required-children として報告)だけで、キーが何かをするかも、行が階層を伝えているかも確かめません。
この診断ツールは、ページをTabキーで進みます。表のセルの中で行を開く矢印のうち、クラス名(toggle、expander、DataTablesの dt-control など)とポインターのカーソルから分かるものに、Tabキーが一度も届かなければ報告します。DataTablesのように、そのクラスがセルそのものに付いている場合は、そのセルを報告します。行全体でTabの移動先が1つだけのツリーグリッドでは、その移動先にフォーカスを置いて下、上、右の矢印キーとEnd、Homeを押し、フォーカスがまったく動かなければ、ほかの行に届かないとして報告します。何もフォーカスを受けないツリーグリッドも報告します。aria-level を持つ行のうち、次の行が1段深くて隠れているのに aria-expanded がないものも報告します。複数のリンクがTabの移動先で、矢印キーを押しても何も起きないツリーグリッドは、人が確認する項目として渡します。2つ目の表がこれに当たります。それと分かるクラス名のない矢印は見つからず、階層が聞く人にとって筋が通っているかは、どの確認でも分かりません。
2つ目の表は、AIによる修正でできてしまうことがある形です。role="treegrid" を加えただけで終わります。spanに role="button" と tabindex="0" を付ける修正もあり、Tabキーでは届くようになりますが、EnterキーとSpaceキーは何もしないままです。Tabキーで到達できないdivのボタンで説明しているとおりです。
試してみる
キーボードだけで「夏のセールを企画する」を開き、「メールマガジンの枠を押さえる」まで移動してみましょう
問題1: Tabキーを押します。止まるのは表示中のタスクのリンクだけで、矢印には止まらないため、「夏のセールを企画する」を開けません。
問題2: 同じ表に role="treegrid" を足しただけです。Tabキーは同じように2つの矢印を飛ばし、矢印キーを押しても何も起きず、下の欄では行の階層も、タスクが開いているかどうかも一度も読まれません。
修正を適用します。 ボタンでは、Tabキーで各矢印に届き、Enterキーでタスクが開きます。ツリーグリッドでは、矢印キーで行を移動してタスクを開け、右矢印キーで行のセルへ入り、HomeとEndで端へ移動できます。
問題あり 修正できますか?
タスク
| タスク | 担当者 | 期限 |
|---|---|---|
| 春のカタログを公開する | Ana Ruiz | 3月14日 |
| 商品説明を書く | Ben Carter | 3月7日 |
| 商品写真を撮る | Dana Park | 3月10日 |
| ドメインを更新する | Eli Novak | 3月20日 |
| 夏のセールを企画する | Chris Moreau | 4月2日 |
| 割引を決める | Ana Ruiz | 3月25日 |
| メールマガジンの枠を押さえる | Ben Carter | 3月28日 |
スクリーンリーダーの読み上げ:
(Tabで表へ移動)
修正方法
キーボードで行を開けるようにし、開閉の状態を伝える
クリックにしか反応しない矢印
<h2 id="tasks-title">タスク</h2><table class="tasks" aria-labelledby="tasks-title"> <thead> <tr> <th scope="col">タスク</th> <th scope="col">担当者</th> <th scope="col">期限</th> </tr> </thead> <tbody> <tr id="task-101" class="is-open"> <td> <span class="toggle"></span> <a href="/tasks/101">春のカタログを公開する</a> </td> <td>Ana Ruiz</td> <td>3月14日</td> </tr> <tr class="subtask" data-parent="task-101"> <td> <a href="/tasks/102">商品説明を書く</a> </td> <td>Ben Carter</td> <td>3月7日</td> </tr> <tr class="subtask" data-parent="task-101"> <td> <a href="/tasks/103">商品写真を撮る</a> </td> <td>Dana Park</td> <td>3月10日</td> </tr> <tr> <td> <a href="/tasks/104">ドメインを更新する</a> </td> <td>Eli Novak</td> <td>3月20日</td> </tr> <tr id="task-105"> <td> <span class="toggle"></span> <a href="/tasks/105">夏のセールを企画する</a> </td> <td>Chris Moreau</td> <td>4月2日</td> </tr> <tr class="subtask" data-parent="task-105" hidden> <td> <a href="/tasks/106">割引を決める</a> </td> <td>Ana Ruiz</td> <td>3月25日</td> </tr> <tr class="subtask" data-parent="task-105" hidden> <td> <a href="/tasks/107">メールマガジンの枠を押さえる</a> </td> <td>Ben Carter</td> <td>3月28日</td> </tr> </tbody></table><a href="/tasks/completed">完了したタスク</a>問題点3点
- 矢印はフォーカスを受けないspanで、
clickにしか反応しないため、閉じたタスクのサブタスクにはキーボードで届きません。 - タスクが開くことも、開いているかどうかも、マークアップのどこにも書かれていません。
- 後に続く行がそのタスクのサブタスクであることも書かれていません。入れ子は余白でしかありません。
行にボタンを置き、サブタスクであることを名前で伝える
<h2 id="tasks-title">タスク</h2><table class="tasks" aria-labelledby="tasks-title"> <thead> <tr> <th scope="col">タスク</th> <th scope="col">担当者</th> <th scope="col">期限</th> </tr> </thead> <tbody> <tr id="task-101"> <td> <button type="button" class="toggle" aria-label="サブタスク" aria-expanded="true"></button> <a href="/tasks/101">春のカタログを公開する</a> </td> <td>Ana Ruiz</td> <td>3月14日</td> </tr> <tr class="subtask" data-parent="task-101"> <td> <a href="/tasks/102">商品説明を書く<span class="visually-hidden">、サブタスク</span></a> </td> <td>Ben Carter</td> <td>3月7日</td> </tr> <tr class="subtask" data-parent="task-101"> <td> <a href="/tasks/103">商品写真を撮る<span class="visually-hidden">、サブタスク</span></a> </td> <td>Dana Park</td> <td>3月10日</td> </tr> <tr> <td> <a href="/tasks/104">ドメインを更新する</a> </td> <td>Eli Novak</td> <td>3月20日</td> </tr> <tr id="task-105"> <td> <button type="button" class="toggle" aria-label="サブタスク" aria-expanded="false"></button> <a href="/tasks/105">夏のセールを企画する</a> </td> <td>Chris Moreau</td> <td>4月2日</td> </tr> <tr class="subtask" data-parent="task-105" hidden> <td> <a href="/tasks/106">割引を決める<span class="visually-hidden">、サブタスク</span></a> </td> <td>Ana Ruiz</td> <td>3月25日</td> </tr> <tr class="subtask" data-parent="task-105" hidden> <td> <a href="/tasks/107">メールマガジンの枠を押さえる<span class="visually-hidden">、サブタスク</span></a> </td> <td>Ben Carter</td> <td>3月28日</td> </tr> </tbody></table><a href="/tasks/completed">完了したタスク</a>修正の手順5ステップ
- 矢印を「サブタスク」という名前のHTML標準のボタンにします。Tabキーで届き、EnterキーとSpaceキーで、スクリプトがもともと待っているクリックとしてタスクを開けます。
- ボタンに
aria-expandedを付け、クリックの処理で切り替えます。スクリーンリーダーは、タスク自身のリンクの直前で「サブタスク、ボタン、折りたたみ」や「展開」と読み上げます。 - 各サブタスクのリンクの最後に見えない言葉を加えます。Tabキーでたどり着いても、「タスク」の列を下へ移動しても「商品説明を書く、サブタスク」と読まれます。名前は画面上の言葉で始まったままなので、音声操作の利用者はその言葉で呼べます。
- 閉じたタスクの行には
hiddenを残し、Tabの順序から外します。開いたタスクの行はボタンのすぐ後に続くため、次のTabキーでその中を進みます。 - 表の見出しはそのまま残し、矢印はクラスではなく
aria-expandedから描きます。
これで二つの修正前の例はどちらも直り、1.3.1、2.1.1、4.1.2を満たします。表のライブラリーには、すでにこの形で矢印を描くものもあります。たとえばElement Plusは、矢印を aria-expanded 付きのボタンとして出力します。
この方法を選ぶ理由:まずHTMLの要素を使う
どちらも基準を満たします。この例のように、開いて読んで閉じる表には、ボタンを使ってください。どのブラウザー、スクリーンリーダー、音声操作のソフトも知っているHTML標準のコントロールが、タスクごとに1つあるだけで、スクリプトは数行で済みます。ほかの表と同じように、Tabキーで移動でき、スクリーンリーダーの表のキーで読めます。ツリーグリッドを作るのは、各行の状態や担当者を変えていく問い合わせの一覧のように、表が行を順に処理していく作業の場で、行の中に操作するセルがあり、しかも行が多くてTabで1つずつ移動してはいられない場合です。そのときは、ロール、階層と位置、状態、Tabの移動先1つ、行とセルのすべてのキーをそろえます。
DataTablesの例のように、子の行ではなく1つの詳細欄が開く行にも、同じボタンを使います。行の最初のセルにボタンを置いて aria-expanded を付け、詳細は次の行に、すべての列にまたがる1つのセルで表示します。そうすれば、詳細はそれが属する行のすぐ後に読まれます。
ボタンには、開く中身を表す名前を付けます。「サブタスク」は短く、同じセルでタスクの名前が続くため、セルごとに移動するスクリーンリーダーでは両方が聞こえます。音声操作で「サブタスクをクリック」と言うと、該当するボタンすべてに番号が付き、そこから選ぶことになります。「『夏のセールを企画する』のサブタスク」のようにタスク名を含めれば直接1つに届きますが、タスク名を2回聞くことになります。ボタンとリンクは横に並べ、一方をもう一方の中に入れないでください。HTMLではボタンの中にリンクを入れられず、ボタンの中にあるものは、スクリーンリーダーにはボタンの名前の一部としてしか読まれません。
ツリーグリッドでは、ARIA Authoring Practicesのメール受信トレイの例が、行に先にフォーカスを置き、セルへは右矢印キーで入ります。セルにフォーカスを置けないツリーグリッドは、ツリーグリッドではなくツリービューだとも書かれています。チェックボックスや削除ボタンのようなコントロールがあるセルでは、セルではなくそのコントロールがフォーカスを受けます。ここでのリンクと同じです。aria-selected は、複数のタスクにチェックを入れてから「アーカイブ」を押すときのように、行を選ぶことと開くことが別の段階になるツリーグリッドのためのものです。複数の行を選べるときは、treegridに aria-multiselectable="true" を付けます。
見えている行だけをページに置く表や、タスクを開いたときにサブタスクを読み込む表では、ページにない行をブラウザーは数えられません。ツリーグリッドでは、ページに置く行にはすべて aria-level、aria-posinset、aria-setsize を付けたままにし、treegridに aria-rowcount を、各行に aria-rowindex を付けます。ライブラリーの表の部品も、サイトが写す例も、作りはさまざまです。使う前に、矢印がボタンで、行が開いているかどうかを伝え、その下の行が子の行であることを伝えているかを確かめます。
デザインの案
控えめに描いた行も、キーで開ける
サブタスクが開くタスクを5通りに描きました。それぞれをマウスで開いてから、マウスを使わずにTabで移動してみてください。3つはキーボードで開け、2つはマウスでしか開けません。
デザインファイルで
- 開く行は、閉じた状態と開いた状態の2通りを、それぞれの向きの矢印とともに描く。
- 矢印にフォーカスの枠を描き、ホバーしなくても矢印が見えるようにする。
- 矢印の横に「サブタスク」のような名前を書く。スクリーンリーダーが読む名前になる。
- タスクの下の行が子の行であることを、字下げだけでなく言葉でどう伝えるかを書く。
合格
タスク 期限 夏のセールを企画する 4月2日 割引を決める、サブタスク 3月25日 メールマガジンの枠を押さえる、サブタスク 3月28日 ドメインを更新する 3月20日 丸いボタンに入れた山形の矢印 矢印を直径28pxの円に入れ、ホバーで薄い灰色に塗り、フォーカスで枠を付けます。行頭の記号ではなく、押せるものに見えます。行が開くと下を向き、ポインターを重ねていなくても常に表示します。タスク 期限 夏のセールを企画する 4月2日 割引を決める、サブタスク 3月25日 メールマガジンの枠を押さえる、サブタスク 3月28日 ドメインを更新する 3月20日 件数のピルで行を開く 名前の後のピルに「サブタスク2件」と件数を表示し(#eef2ffの上に#3730a3、8.88:1)、開くと向きが変わる小さな矢印を添えます。開く前に中身が分かり、ピルの言葉がそのまま、スクリーンリーダーの読む名前にも、音声操作で呼ぶ名前にもなります。タスク 期限 夏のセールを企画する 4月2日 サブタスク 割引を決める 3月25日 サブタスク メールマガジンの枠を押さえる 3月28日 ドメインを更新する 3月20日 細い線でつなぎ、小さな文字でサブタスクと示す 開いた行は、タスクの下の細い線から下がり、それぞれの先頭に小さな灰色の文字(#5f6368、白に対して6.05:1)で「サブタスク」と表示します。字下げが示すことを文字でも伝えるため、字下げに気づかない人にも分かり、スクリーンリーダーで行を読み進めたときにも同じ言葉が聞こえます。
不合格
タスク 期限 夏のセールを企画する 4月2日 割引を決める 3月25日 メールマガジンの枠を押さえる 3月28日 ドメインを更新する 3月20日 ポインターを重ねたときだけ現れる矢印WCAG 2.1.1 行にポインターを重ねるまで表はすっきりしたままで、重ねると名前の前に矢印が現れます。ほかに表示する方法がないため、Tabでは止まらず、タッチスクリーンにはそもそもホバーがありません。矢印を常に表示するか、キーボードでフォーカスしたときとタッチスクリーンでは常に表示します。タスク 期限 夏のセールを企画する 4月2日 割引を決める 3月25日 メールマガジンの枠を押さえる 3月28日 ドメインを更新する 3月20日 矢印がなく、行全体のクリックで開くWCAG 2.1.1 いちばんすっきりした表です。矢印はなく、行のどこをクリックしても開き、ホバーで行に色が付くことだけが手がかりです。行はコントロールではないため、Tabでは飛ばされ、開くことも伝わりません。行全体でクリックを受ける場合も、操作の対象として、目に見える矢印か件数を描きます。
修正の確認
5項目、マウス不要
確認方法: キーボード、スクリーンリーダー、強制配色、拡大表示
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。表、その部品、ライブラリーを変更した際は、再度確認します。
スクリーンリーダーでの aria-expandedの読まれ方
制限
このサンプルは、どのアプリケーションでも同じ結果になることを保証するものではありません。スクリプトは最小限の例で、ツリーグリッドにはPage UpとPage Down、選択、先頭文字での移動、必要なときのサブタスクの読み込みがありません。また、Tabキーを押すとどの行からでもグリッドの外へ出ます。パターンでは、同じ行に次のリンクがあれば、まずそこへ移ります。列のない、セクションとページのサイドバーはツリービューで、キーボードで開けず、階層も伝わらないツリービューで扱っています。セルをその場で編集する表は、キーボードでセルを移動・編集できないデータグリッドで扱っています。1つだけ開閉する部分は、開いているか伝わらない表示・非表示ボタンで扱っています。対象とする支援技術で、一連の操作全体をテストしてください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
参考資料
- ARIA APG: treegrid(新しいタブで開きます)
- ARIA APG: treegrid-1(新しいタブで開きます)
- ARIA APG: disclosure(新しいタブで開きます)
- ARIA APG: keyboard-interface(新しいタブで開きます)
- WCAG Understanding: info-and-relationships(新しいタブで開きます)
- WCAG Understanding: keyboard(新しいタブで開きます)
- WCAG Understanding: name-role-value(新しいタブで開きます)
- WCAG Technique F15(新しいタブで開きます)
- WCAG Technique F59(新しいタブで開きます)
- w3.org: wai-aria-1.2(新しいタブで開きます)
- datatables.net: row_details(新しいタブで開きます)
EaseWebのサービス
修正した問題を、次のリリースで再発させない
このガイドのような修正方法を、コーディングエージェントが従うルールと、CIのチェックに組み込みます。あわせて定期的に、キーボードとスクリーンリーダーで手動の再テストを行います。