トピックを検索
キーボードでは系列を切り替えられないグラフの凡例
凡例の項目をクリックすると系列が隠れるが、項目はキャンバスに描いた絵かただのリストの項目なので、Tabキーでは移動できず、押せることも伝わらない
li.onclick = () => { 問題
キーボードやスクリーンリーダーの利用者は、系列を隠してほかの系列を読めず、凡例が操作できることも伝わらない
ネットショップの管理画面に、日別の注文数の棒グラフがあり、今週の棒と先週の棒が並んでいます。凡例には二つの系列の名前が、それぞれの色の見本と一緒に並んでいます。「先週」をクリックすると先週の棒が消え、今週の棒だけを読めるようになります。凡例の項目には、もう一度クリックするまで取り消し線が引かれます。Chart.js では、ショップ側でコードを書かなくても、初めからこう動きます。
マウスを使わずにTabキーを押すと、フォーカスはカードの上から、グラフの下の「すべての注文を見る」へ一気に移ります。凡例の項目には一度も止まらないため、キーボードでは先週の棒を消せません。スクリーンリーダー利用者にとって、凡例はキャンバスに描かれていれば何もないのと同じで、リストの項目で作られていれば「今週」「先週」という2行の文字でしかありません。どちらでも、項目には押せることを示す役割も、押した後にどの系列が隠れているかを示す状態もありません。
WCAG 2.1.1「キーボード」は、ポインターでできることはすべてキーボードでもできるよう求めています。系列を隠すのはこのグラフの機能の一つですが、クリックでしか実行できません。Chart.js の標準の凡例では、項目は要素ですらありません。ライブラリーが項目をキャンバスに描き、ポインターの位置からどの項目がクリックされたかを割り出しています。キャンバスは初期状態ではフォーカスを受け取らず、キーの入力を待つ処理もありません。二つ目の作り方では凡例をHTMLにしています。Chart.js の公式サンプル「HTML Legend」と同じで、系列ごとに li を作り、onclick とポインターのカーソルを付けています。クリックの処理はどの要素にも付けられますが、Tabキーで移動できるのはリンク、ボタン、フォームの入力欄などだけなので、リストの項目は飛ばされます。どちらの作り方でも、系列を隠せるのはポインターだけです。失敗例F54は、これを onmousedown のようなポインター専用のイベントハンドラーで説明しています。click のハンドラー自体はポインター専用ではなく、ボタンならキーボードでも発生しますが、Tabキーで届かない要素では、ポインターでしか発生させられません。
WCAG 4.1.2「名前(name)・役割(role)及び値(value)」は、すべてのコントロールの種類、名前、状態を支援技術に伝えるよう求めています。系列を隠す凡例の項目は、系列が表示されているか隠れているかという状態を持つコントロールです。キャンバスに描かれた項目には、名前も役割も状態もありません。li で作った項目には文字という名前はあるものの、役割はリストの項目で、状態は text-decoration で引いた線という見た目でしかなく、支援技術に状態として伝わるものではありません。これは失敗例F59にあたります。F59は div や span の例ですが、スクリプトで要素をコントロールにしながら、コントロールの役割を与えていない点は同じです。
系列を隠せる凡例は、混み合ったグラフで2本の線を比べたり、大きな系列の陰で小さく見える系列を読んだりするための手段です。使えなければ、グラフはすべての系列を一度に見せたままで、凡例で読みやすくなるはずの部分も読みにくいままです。
10グループ中2グループに影響
キャンバスに描いた凡例は、ページの中に調べられるものを何も残しません。axe にはキャンバスが見えるだけで、role="img" と名前があれば合格にします。クリックの処理を付けたリストの項目もマークアップとしては正しく、axe はクリックの処理までは調べないため、これも合格にします。この診断のキーボードのチェックは、クラスやidで凡例と分かる要素の中を調べ、ポインターのカーソルかスクリプトで設定した onclick でクリックを受け付けるのにTabキーでは届かない項目があれば、問題として報告します。Chart.js の「HTML Legend」のサンプルどおりに作った凡例が、これにあたります。キャンバスに描いた凡例はこの方法では見つからないため、ライブラリーが描いたグラフの中に、フォーカスを受け取るものが一つもなければ、確認候補として出します。凡例や点をクリックしてから、同じことをキーボードで試すのは人の役割です。独自の凡例をAIのアシスタントに頼むと、li.onclick も含めて、ライブラリーのサンプルをそのまま写すことがあります。
試してみる
先週の棒をマウスで隠し、次にキーボードだけで試してみましょう
問題1: グラフの上にある「先週」をクリックします。次にマウスを使わずにTabキーを押してみてください。フォーカスは凡例を通り過ぎ、「すべての注文を見る」へ移ります。
問題2: 凡例がグラフの下に移りました。「先週」をクリックしてからTabキーを試し、下の欄を読んでみてください。ボタンとして読まれますか?オフになったことは伝わりますか?
修正を適用します。 Tabキーで「先週」へ移動し、Spaceキーを押します。トグルボタンならEnterキーでも押せます。各項目をスクリーンリーダーがどう読むかは、下の欄に出ます。
問題あり 修正できますか?
日別の注文数
グラフの後を読むと、スクリーンリーダーの読み上げ:
- すべての注文を見る、リンク
修正方法
凡例の各項目を、オンかオフかを伝えるコントロールにする
Chart.js がキャンバスに描く凡例
<section class="card"> <h2>Orders per day</h2> <canvas id="orders" role="img" aria-label="…"></canvas> <a href="/orders">View all orders</a></section>問題点2点
- 凡例はキャンバスに描かれているため、Tabキーは「This week」にも「Last week」にも止まらず、系列を隠せるキーもない。
- 項目はピクセルでしかなく、スクリーンリーダーが読める名前も役割も状態もない。
系列ごとのトグルボタン
<section class="card"> <h2>Orders per day</h2> <canvas id="orders" role="img" aria-label="…"></canvas> <ul class="legend" id="legend" aria-label="Series shown"></ul> <a href="/orders">View all orders</a></section>修正の手順5ステップ
- Chart.js がキャンバスに描く凡例を
plugins.legend.display: falseでオフにし、凡例をページの中の一つだけにする。 - 各リストの項目に
buttonを置き、名前は系列名の「This week」「Last week」だけにする。Tabキーで届き、EnterキーとSpaceキーで押せる。 - ボタンに
aria-pressed="true"を付け、押すたびに、系列が表示されているかどうかに合わせて設定し直す。スクリーンリーダーは、たとえば「Last week、トグルボタン、押下」と読み、棒が隠れると「押下なし」と読む。 - 隠れた系列の取り消し線は、インラインのスタイルではなく
aria-pressed="false"から描き、見た目と読み上げを一つの値から決める。 - リストに「Series shown」という名前を付け、どの系列が表示されているかを伝えるボタンのまとまりとして聞こえるようにする。
これで二つの修正前の例はどちらも直り、2.1.1と4.1.2を満たします。色の見本と名前、オフのときの取り消し線という凡例の見た目はそのままです。
この方法を選ぶ理由:まずHTMLの要素を使う
どちらの方法でも基準を満たします。トグルボタンは、色の見本と名前という描いたとおりの凡例を保ち、キーボード操作と状態だけを加えるため、凡例がデザインの一部になっているグラフに向いています。ただ、押下状態はチェックボックスほど知られていないため、何が押下を意味するのかをリストの名前で伝えています。チェックボックスは、誰もが知っている形で、押すと何が起きるのかをはっきり伝える代わりに、見た目が変わります。系列が多く、そこから選ぶ管理画面のように、凡例がすでに絞り込みの役割を持っている場合に選んでください。
名前は変えない。 ボタンは、系列が表示されていても隠れていても「Last week」のままで、変わるのは aria-pressed だけです。文字まで「Show last week」に変えると、スクリーンリーダーには状態が二通りの方法で伝わり、互いに矛盾することもあります。ARIA Authoring Practicesのボタンのパターンも、トグルボタンの状態が変わってもラベルを変えないことが重要だとしています。
リストの項目を補修する場合。 li に role="button"、tabindex="0"、aria-pressed、EnterキーとSpaceキーの処理を加えても基準は満たせます。ただ、button ならそのすべてが初めから備わっていて、補修は途中で抜けが出やすくなります。
最後に残った系列。 すべての系列を隠すと、グラフは目盛りだけの空の枠になります。最後の一つは押しても隠れないようにするか、グラフの場所に、表示している系列がないことを書いて伝えます。
グラフのテキストの代替。 系列を隠して変わるのは描く内容で、データではありません。グラフの名前と表は、スクリーンリーダーで数値が伝わらないグラフのとおり、すべての系列を伝えるままにしておきます。スクリーンリーダー利用者は、凡例を使わなくても数値にたどり着けます。
ほかのライブラリー。 Highcharts のアクセシビリティモジュールは、凡例も含めた独自のキーボード操作を備えているため、別の凡例を作る前に、何が用意されているかを確かめてください。凡例を、ポインターのカーソルを付けた div で描くライブラリーは、ここのリストの項目と同じ問題を抱えていて、同じ修正が当てはまります。
ホバーしたときだけツールチップに出る値は、キーボードでは表示できないグラフのツールチップで扱っています。クリックするとページを絞り込む棒は、同じ問題の別の形ですが、まだガイドはありません。色の見本だけで系列を見分けさせ、名前を添えない凡例は、1.4.1「色の使用」の問題です。ステータスの印での例を色だけで区別されるステータス表示で扱っています。
修正の確認
4項目、マウス不要
確認方法: キーボード、スクリーンリーダー
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。グラフのコンポーネントやライブラリーを更新した際は、再度確認します。
スクリーンリーダーでの aria-pressedの読まれ方
制限
このサンプルは、どのグラフでも同じ結果になることを保証するものではありません。Chart は Chart.js のことで、setDatasetVisibility は系列を隠すための Chart.js の機能です。ほかのライブラリーでは、それにあたる機能に置き換えてください。aria-pressed の読み方はスクリーンリーダーによって異なり、「トグルボタン」と読むものもあれば、「ボタン」の後に押下状態を読むものもあります。管理画面全体を、キーボードと対象とする支援技術で確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
EaseWebのサービス
修正した問題を、次のリリースで再発させない
このガイドのような修正方法を、コーディングエージェントが従うルールと、CIのチェックに組み込みます。あわせて定期的に、キーボードとスクリーンリーダーで手動の再テストを行います。