タブUIをアクセシブルにする
どうも、nano72mknです。
アクセシビリティを意識してタブUIを作ったので、実装時に調べたことやポイントをまとめます。
タブUIについて
まず、初めにタブUIと言われて思い浮かべるのは、この形だと思います。
このUIは、2つのパーツに分けることができます。
1つ目は、「タブ」と呼ばれるパーツ
2つ目は、「タブパネル」と呼ばれるパーツ
この2つのパーツをがっちゃんこして、タブUIは出来ています。
タブUIをアクセシブルにする
roleとaria属性を付与してアクセシビリティ対応をする。
roleを付与する
付与する必要があるものは下記の3つ
tab
tablist
tabpanel
tabとtablist
タブにはtab
、複数のタブを囲っている要素にはtablist
のroleを付与する
<div id="tab">
<div role="tablist">
<button role="tab">Tab1</button>
<button role="tab">Tab2</button>
<button role="tab">Tab3</button>
</div>
...
<div>
tabpanel
タブパネルにtabpanel
のroleをつける
<div id="tab">
...
<div role="tabpanel">Tab Panel 1</div>
<div role="tabpanel">Tab Panel 2</div>
<div role="tabpanel">Tab Panel 3</div>
</div>
支援技術に対応する
何のタブなのか、タブの状態はどうなっているかを支援技術に伝えるために、aria属性を付与します
tablist
にaria-label
をつける
タブにフォーカスした際に何のタブグループなのかを知らせるためのaria-label
を設定します。
<div id="tab">
<div role="tablist" aria-label="hoge">
<button role="tab">Tab1</button>
<button role="tab">Tab2</button>
<button role="tab">Tab3</button>
</div>
...
<div>
上記の実装で、「Tab1」にフォーカスがあたった際に 「Tab1、選択中、タブ、1/3、hoge、 タブグループ」 と読み上げてくれます。
※ 「hoge」がaria-label
で指定したテキスト
tab
にaria-selected
をつける
どのタブが選択されているかを判定できるようにするために、aria-selected
を付与します。
また、別のタブを選択したときにaria-selected
の状態を切り替えます。
<div id="tab">
<div role="tablist" aria-label="hoge">
<button role="tab" aria-selected="true">Tab1</button>
<button role="tab" aria-selected="false">Tab2</button>
<button role="tab" aria-selected="false">Tab3</button>
</div>
...
<div>
tab
とtabpanel
を紐づける
紐づける為にやることが2つあります
-
tab
にaria-controls
を指定 -
tabpanel
にaria-labelledby
を指定
<div id="tab">
<div role="tablist" aria-label="hoge">
<button
role="tab"
id="tab-1"
aria-selected="true"
aria-controls="tabpanel-1">
Tab1
</button>
...
</div>
<div
role="tabpanel"
id="tabpanel-1"
aria-labelledby="tab-1">
Tab Panel 1
</div>
...
<div>
aria-labelledby
の影響で、タブパネルにフォーカスを当てたときにも 「本文、Tab1、タブパネル」 とタブのラベルも読み上げてくれます。
tabpanel
にdisplay: none;
をつける
表示されているタブパネル以外にはdisplay: none;
を付与し非表示にします。
<div id="tab">
...
<div role="tabpanel">Tab Panel 1</div>
<div role="tabpanel" style="display: none;">Tab Panel 2</div>
<div role="tabpanel" style="display: none;">Tab Panel 3</div>
</div>
※display: none;
を付与しているときは、すでにアクセシビリティツリーから削除されているためaria-hidden
は不要です。
キーボードで操作できるようにする
WCAGの達成基準2.1.1で下記のように言われております。
達成基準 2.1.1 キーボード (レベル A): コンテンツのすべての機能は、個々のキーストロークに特定のタイミングを要することなく、キーボードインタフェースを通じて操作可能である。ただし、その根本的な機能が利用者の動作による終点だけではない軌跡に依存する入力を必要とする場合は除く。
この項目はレベルA(最低基準)なので、対応していきます。
タブUIでクリアするべきキーボードの操作は4つあり、JSで実装します。
tab
のフォーカスを左右キーで移動させる
左右の矢印キーを押した際に、タブのフォーカスを移動するようにします。
「Tab1」にフォーカスがあった場合に左キーを押したときには「Tab3」へ
「Tab3」にフォーカスがあった場合に右キーを押したときには「Tab1」へフォーカスが移るようにします。
vue.jsであれば、@keydown.right
と@keydown.left
を使うとサクッとできます。
tablist
の外から移動してくる場合、アクティブなtab
に移動させる
フォーカスがこれを実現するためには、tabindex
を調整する必要があります。
tabindex
は、-1
が指定されていると順次ナビゲーションでは到達できなくなるので、その性質を使います。
<div id="tab">
<div role="tablist" aria-label="hoge">
<button role="tab" aria-selected="false" tabindex="-1">Tab1</button>
<button role="tab" aria-selected="true" tabindex="0">Tab2</button>
<button role="tab" aria-selected="false" tabindex="-1">Tab3</button>
</div>
...
<div>
アクティブなタブはtabindex
を0にし、それ以外はtabindex
を-1にしてフォーカスが当たらないようにします。
外の要素から「Tab1」をスキップし、「Tab2」にフォーカスが移動することを確認できます。
tab
にある場合、紐づいているtabpanel
に移動させる
フォーカスがアクティブなtabindex="0"
を付与するだけで対応できます。
<div id="tab">
...
<div role="tabpanel" tabindex="0">Tab Panel 1</div>
<div role="tabpanel" tabindex="0">Tab Panel 2</div>
<div role="tabpanel" tabindex="0">Tab Panel 3</div>
</div>
フォーカスが「Tab2」から「Tab3」ではなく、直接紐づいているタブへ移動しているのを確認できます。
tab
にフォーカスが当たっている時にDeleteキーを押したら削除
おまけ: tab
が削除可能な場合は、Deleteキーで選択されているタブを削除できるようにする必要があります。
まとめ
上記対応を行うと、下記codepenのようになります。
以上が、タブUIをアクセシブルにするためのポイントでした。
UIライブラリを使っていると、わからないような細かい対応もあり、とても楽しく実装できました。
また、何か間違いや足りない対応などがありましたら、お気軽にコメントしていただけると嬉しいです!
最後まで読んでいただきありがとうございました。
参考
サクッと説明しちゃっているので、詳しく知りたい方は、下記参考記事を読んでみてください!
Discussion
こんにちは!
アクセシビリティを意識されていて素晴らしい実装だと思います!
提案として、非表示は
display:none
ではなくhidden="until-found"
を利用するのをオススメします。until-found
の存在を初めて知りました!ページ内検索で引っかけることが出来る事ができるのは便利ですし、
safari等非対応環境でも既存の
hidden
と同じ挙動なのも安心ですねとても、勉強になります!
もっと
until-found
について調べてみます!ありがとうございました🙇