タブ切り替えを実装する時の注意点
JavaScript, Accessibilityタブ切り替えを実装する際に躓いたところ。
タブの定義
https://www.w3.org/TR/wai-aria-practices/#tabpanel
(キーボード操作については省略)
tablistのaria-label
role="tablist"
を持つ要素が、VoiceOver 上でグループとして認識されなかった。
対処法としては、tablistの要素に aria-label
属性を使って適切なグループ名を与える。
これで、グループとして読み上げられるようになる。
aria-label
はあらゆる要素、role
に使用することができる。
aria-label 属性の使用 - アクセシビリティ | MDN
tabの選択
タブのwrapperとしてよく ul
要素が使われる。
ただし、下記のマークアップを行うと、支援技術のコントロールが li
の中に入るまでボタンが押せない。
<ul role="tablist">
<li role="tab" aria-controls="panel01" aria-selected="false">
<button type="button">タブ1のボタン</button>
</li>
...省略
</ul>
li
はフォーカス対象外の要素なので、tabキーでフォーカスすることはないけれど、
role="tab"
がついているので、支援技術のコントロール対象になってしまっている。
li
ではなく、中の button
要素に role="tab"
を指定することで回避できた。
そもそも ul
要素にする必要がなく、実際WAI-ARIA Authoring Practicesだと div
要素 1 つで囲んでいる。
以下の記事でも ul
を使用しているけど、 li
にrole="presentation"
を指定することで役割を消している。
WAI-ARIA対応のタブ型UIの作り方(React編) - ICS MEDIA
aria-controlsのエラー
下記のコードは a11y のチェッカーでエラーになる。
<>
<div role="tablist" aria-label="タブ選択">
{items.map(item => (
<button
role="tab"
aria-controls={item.id}
aria-selected={item.id === panel.id}
onClick={setPanelId}
type="button"
>
タブ{item.id}
</button>
))}
</div>
<div role="tabpanel" id={`panel${panel.id}`}>パネル{panel.id}だよ</div>
</>
aria-controls
に関わらず、id を参照するものは要素があらかじめ存在する必要がある。
Accessibility Tree が DOM を元に生成する都合からだと思われる。
上記のパネルの id は動的に変更され、aria-controls
も変更先のものを参照できない。
対処法としてはあらかじめ全てのパネル要素を生成させた上で、
aria-hidden
属性や、CSS で display: none;
や visibility: hidden;
で非表示にするぐらい。
参照が id というのもネックだったりするけど、 将来的には Accessibility Object Model で対応できそう。
Accessibility Object Model の日本語訳と HTML5 Conference 2018 登壇 | masuP.net
masuP 氏による Accessibility Object Model 日本語訳