タブ切り替えを実装する時の注意点
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 日本語訳