トピックを検索
キーボードでは表示できないグラフのツールチップ
棒グラフの件数は、ポインターを棒に重ねている間だけ Chart.js が描くツールチップにしか出ず、キャンバスはフォーカスも受け取らないため、キーボードでは件数を出す手段がない
問題
キーボード利用者は、ポインターを棒に重ねたときのツールチップにしか出ない件数を読めない
ネットショップの管理画面に、先週の日別の注文数を表す棒グラフがあります。棒には数字が書かれていません。土曜日の件数を知るには、土曜日の棒にポインターを重ねます。すると小さな黒いツールチップが開き、「土」と「注文数: 31」が出ます。棒から外れると閉じます。Chart.js では、ショップ側でコードを書かなくても、初めからこう動きます。
マウスを使わずにTabキーを押すと、フォーカスはカードより前にある要素から、グラフの下の「すべての注文を見る」へ一気に移ります。グラフにはフォーカスを受け取るものがなく、ツールチップを開くキーもないため、件数は一度も表示されません。キーボード利用者には、土曜日が一番多かったことは見えても、31件だったことや、火曜日が15件か14件かは分かりません。頼りになるのは、5件ごとに引かれた目盛りの線だけです。
グラフにはテキストの代替もあります。スクリーンリーダーで数値が伝わらないグラフの一つ目の修正のとおり、名前にすべての曜日の件数が書かれているので、スクリーンリーダー利用者には件数が聞こえます。ただ、その文字は画面には出ないため、目で見ながらキーボードを使う人には届きません。
WCAG 2.1.1「キーボード」は、ポインターでできることはすべてキーボードでもできるよう求めています。棒の件数を表示するのはこのグラフの機能の一つですが、それができるのはポインターだけです。Chart.js はツールチップをキャンバスに描き、events オプションに並ぶブラウザーのイベントで開きます。初期値は mousemove、mouseout、click、touchstart、touchmove で、どれもポインターか指によるものです。初期設定では、ツールチップが開くのはポインターが重なっている棒だけです。キャンバスは初期状態ではフォーカスを受け取らず、グラフはキー入力も受け付けていないので、キーボードでできることが何もありません。これは失敗例F54にあたります。値を表示するという機能を、ポインター専用のイベントハンドラーだけに結び付けているからです。
問題になるのは、件数を表示する場所がツールチップしかないことです。棒の上や、グラフの横の表にすでに書かれている数字をツールチップが繰り返しているだけなら、キーボード利用者が見られないものは何もありません。
管理画面を開くのは、正確な数字を知るためです。土曜日の31件と金曜日の24件の差は、週末がまずまずだったか、忙しかったかの違いになります。Chart.js はタッチにも反応するため、タッチ画面では問題なく使えます。件数を一つも見られないのは、キーボードで操作する人と、キーボードの操作に置き換えて動く道具を使う人です。
10グループ中1グループに影響
- 全盲 (影響なし)
- 弱視 (影響なし)
- 色覚 (影響なし)
- ろう (影響なし)
- 難聴 (影響なし)
- 発話 (影響なし)
- 手の操作 (影響あり)
- 筋力・可動域 (影響なし)
- 認知 (影響なし)
- 光過敏 (影響なし)
- 手の操作が限られる キーボードやスイッチの利用者がTabキーを押すと、グラフを通り過ぎてその下のリンクに移り、どのキーでもツールチップは出ません。各曜日の件数は、目盛りの線から見当をつけるしかありません。音声操作の利用者は、マウスグリッドや音声でポインターを棒まで動かして、一本ずつ出すしかありません。
ツールチップは、ポインターを重ねている間だけキャンバスに描かれる絵なので、ページの中にチェックツールが調べられるものはありません。axe には role="img" と名前の付いたキャンバスが見えるだけで、ホバーもしないため、合格にします。この診断は、ライブラリーが描いたグラフの中に、フォーカスを受け取るものが一つもなければ、確認候補として出します。棒にポインターを重ねてから、同じことをキーボードで試すのは人の役割です。ツールチップの中身が、ほかの場所に書かれていない値かどうかは、ページからは判断できません。ツールチップ付きの棒グラフをAIのアシスタントに頼むと、ライブラリーの初期設定をそのまま使い、この修正前のコードと同じものになることがあります。
試してみる
土曜日の注文数をマウスで確かめ、次にキーボードだけで試してみましょう
土曜日の棒にマウスを重ねて、ツールチップを読んでみてください。次にマウスを使わずにTabキーを押すと、フォーカスはグラフを通り過ぎて「すべての注文を見る」へ移り、件数はどこにも出ません。
修正を適用すると、件数が棒の上に表示されます。もう一つの修正では、Tabキーでグラフに止まり、左右の矢印キーでツールチップが動いて、Escapeキーで閉じられます。
問題あり 修正できますか?
日別の注文数
修正方法
ポインターがなくても、すべての件数を読めるようにする
<section class="card"> <h2>Orders per day</h2> <p>Last week</p> <canvas id="orders" role="img" aria-label="…"></canvas> <a href="/orders">View all orders</a></section>問題点2点
- 件数はツールチップの中にしか描かれず、ツールチップは棒にポインターを重ねている間しか開かない。
- キャンバスがフォーカスを受け取らないため、Tabキーはグラフを通り過ぎ、ツールチップを開くキーもない。
棒の上に件数を書く
<section class="card"> <h2>Orders per day</h2> <p>Last week</p> <canvas id="orders" role="img" aria-label="…"></canvas> <a href="/orders">View all orders</a></section>修正の手順3ステップ
- このグラフの
pluginsに chartjs-plugin-datalabels を加え、各棒の値を描かせます。 anchor: 'end'とalign: 'end'を指定し、件数を、視線が自然に向かう棒の先端のすぐ上に置きます。- 件数の色を、白いカードに対して14.68:1の
#1f2937にします。グラフの上にlayout.paddingで20ピクセルの余白を取り、一番高い土曜日の31が上で切れないようにします。
これで修正前の問題が解消し、2.1.1を満たします。ツールチップの中にしかない件数がなくなるので、キーボードで開く必要のあるものが残りません。ツールチップはそのまま残り、棒にポインターを重ねれば開きます。
どちらの方法でも基準を満たします。件数を書く方法は、プラグイン一つとオプション四つで済み、キーボード利用者、画面を拡大している人、ホバーしようと思いつかない人のすべてに一度に役立ちます。この例のように棒が数本のグラフには向いていますが、棒が何十本もあったり系列がいくつも並んだりすると、数字が重なり合って読めなくなります。矢印キーの方法は、描いたとおりの見た目を保ち、点がいくつあっても使えますが、スクリプトと操作の説明の1行が必要で、ロールも正しく付けなければなりません。点の多いグラフや、数字を書くと形が埋もれてしまうグラフで選んでください。スクリーンリーダーで数値が伝わらないグラフのように、件数の表をグラフの横にすでに置いているなら、すべての値はキーボードで届く文字になっていて、ツールチップだけが頼りという状態ではなくなっています。
ここでは role="application" が正しい理由と、ほかではまず正しくない理由。 application は、キーをページに渡すようスクリーンリーダーに伝え、読み上げ用のキーを止めます。読む内容がある部分に付けるのは誤りで、このロールがほとんどの場合に正解にならないのもそのためです。キャンバスの中には読むものがありません。必要なキーはすべてグラフ側で処理し、表示する件数もすべてステータスの行に書き込みます。Recharts 3 も初期設定で同じ作りです。グラフに role="application" と tabindex="0" を付け、矢印キーでツールチップを動かし、そのツールチップをライブリージョンにしています。Recharts 2 では、accessibilityLayer を指定しない限り、この機能はオフでした。
フォーカスで開くツールチップは、フォーカスで表示されるコンテンツ。 グラフがフォーカスを受け取って開くツールチップは、WCAG 1.4.13がホバーやフォーカスで表示されるコンテンツに求めるとおり、フォーカスを動かさずにEscapeキーで閉じられ、閉じられるかフォーカスが離れるまでは開いたままでなければなりません。近づくと消えるツールチップや、下にあるものを覆ってしまうツールチップについては、近づくと消え、閉じられないツールチップで説明しています。
ステータスの行は短く。 キーを押すたびに読まれるので、曜日と件数だけにし、グラフの名前を繰り返さないようにします。
ほかのライブラリー。 Highcharts のアクセシビリティモジュールは、点を順にたどるキーボード操作を加えます。D3 Graph Gallery のツールチップの例のように、mouseover でツールチップの div を表示し、mouseout で隠す手作りのSVGのグラフも、同じ問題を抱えています。修正方法も同じ二つです。各図形の上に text 要素を置くか、グラフ全体をTabキーで一度止まる場所にして、矢印キーで図形の間を移れるようにします。
クリックするとページを絞り込んだり詳細へ進んだりする棒は、同じ問題の別の形ですが、まだガイドはありません。クリックでしか系列を隠せない凡例は、キーボードでは系列を切り替えられないグラフの凡例で扱っています。
デザインの案
ホバーしなくても、グラフの数値は見せられる
ポインターにもキーにも合わせて変わる数値と、ポインターがグラフに乗っている間しか件数を見せない、よく見かける例です。それぞれにマウスを重ねてから、Tabキーと矢印キーでも試してみてください。
デザインファイルで
- ホバーで現れるものだけに件数を置かない。キーボードでもすべての件数を出せるようにするか、グラフに書いておく。
- グラフがポインターに反応するなら、キーボードで同じ状態にしたところも描く。グラフのフォーカスリングと、選ばれている棒。
- どのキーで曜日を移れるかを、左右の矢印キーのようにグラフのそばに書く。
- 件数の文字は、グラフの背景に対して4.5:1以上にする。この例の濃い文字は、白に対して14.76:1。
合格
日別の注文数(先週)
20件日曜日
左右の矢印キーで曜日を移れます。
ポインターとキーに合わせて変わる大きな数値 棒の上に、ふだんは最新の曜日の「20件、日曜日」を表示します。棒にポインターを重ねるか、タップするか、Tabキーでグラフへ移って矢印キーを押すと、数値がその曜日に変わり、棒が濃い青になります。棒の上に何も浮かばず、キーボードでもすべての曜日に届きます。
不合格
日別の注文数(先週)
ホバーで現れる数値WCAG 2.1.1 ふだんは棒だけで、ポインターをグラフに乗せると、各棒の上に件数がふわりと現れます。すっきり見えますが、Tabキーは素通りし、キーボードでは数値を出す方法がありません。
修正の確認
4項目、マウス不要
確認方法: キーボード、スクリーンリーダー
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。グラフのコンポーネントやライブラリーを更新した際は、再度確認します。
スクリーンリーダーでの role="status"、aria-describedby、role="application" tabindex="0"の読まれ方
制限
このサンプルは、どのグラフでも同じ結果になることを保証するものではありません。Chart は Chart.js のことで、setActiveElements はコードからツールチップを開くための Chart.js の機能です。chartjs-plugin-datalabels は、Chart.js のプロジェクトが公開している別のパッケージです。ほかのライブラリーでは、それぞれにあたる機能に置き換えてください。role="application" の扱いはスクリーンリーダーによって異なり、矢印キーを読み上げに使い続けるものもあるかもしれません。管理画面全体を、キーボードと対象とする支援技術で確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
参考資料
- WCAG Understanding: keyboard(新しいタブで開きます)
- WCAG Technique F54(新しいタブで開きます)
- WCAG Understanding: content-on-hover-or-focus(新しいタブで開きます)
- w3.org: wai-aria-1.2(新しいタブで開きます)
- chartjs.org: tooltip(新しいタブで開きます)
- chartjs.org: interactions(新しいタブで開きます)
- chartjs.org: programmatic-events(新しいタブで開きます)
- chartjs-plugin-datalabels.netlify.app: positioning(新しいタブで開きます)
- github.com: v3.0.0(新しいタブで開きます)
EaseWebのサービス
修正した問題を、次のリリースで再発させない
このガイドのような修正方法を、コーディングエージェントが従うルールと、CIのチェックに組み込みます。あわせて定期的に、キーボードとスクリーンリーダーで手動の再テストを行います。