RadioGroup からアクセシビリティを意識したフォーカス管理の実装を学ぶ
こんにちは、株式会社 Gaji-Labo のフロントエンドエンジニア、上條(mk-0A0)です。
最近の記事はアクセシビリティを勉強するなかで学んだことを書いており、前回の記事では APG の Dialog をもとにフォーカス管理とはどのような挙動が求められるのか調べた記事を書きました。
今回は APG の RadioGroup Pattern に記載があったフォーカス管理の実装方法、Roving tabindex と aria-activedescendant が気になったので調べてみました。
RadioGroup の要件
まず RadioGroup とは、複数のラジオボタンが集まったグループのことを指します。フォームなどにおいて項目を選択する役割がありますが、チェックボックスと違って複数選択はできず、基本的に一度選択したら選択は外せない場合が多いです。
Radio Group Pattern | APG | WAI | W3C
APG Pattern にはエディタなどのツールバーにおける RadioGroup についても記載がありますが、今回は割愛します。
有効なキーボード操作は以下です。
- Tab / Shift+Tab:前後のラジオグループに移動
- ←, ↑ / →, ↓:前後のラジオボタンに移動
- Space:チェックをつける・外す
フォーカスの挙動としては、以下のように RadioGroup にフォーカスした際選択した項目へのフォーカスが保たれていることが望ましいとされています。この挙動は input で実装していれば達成できます。
前置きが長くなりましたが、ここから本題です。input を使えば RadioGroup で求められるフォーカスの挙動は達成できると書きましたが、何らかの理由で input が使えず div など他の要素を使う場合もあると思います。実際 APG に掲載されている Example のコードが div で実装されており、先述した Roving tabindex や aria-activedescendant はそのようなケースにおいてフォーカスの挙動を達成するための実装方法のようです。
Roving tabindex と aria-activedescendant
どちらもフォーカス時にアクティブな項目を伝える実装です。
Roving tabindex(ロービング・タブインデックス) とは、アクティブな項目には tabindex="0"
、非アクティブな項目には tabindex="-1"
を付与する実装です。tabindex="-1"
が付与された要素はフォーカスが当たらず、必然的に選択した要素( tabindex="0"
)にフォーカスが当たる仕組みです。あくまでもアクティブな項目を表すためのものなので、フォーカス時のスタイリングやキーボード操作は別途実装が必要になります。Roving tabindex を使う利点の1つは、フォーカス移動した際にその要素までスクロールして表示できることです。
詳しい実装は Example の Codepen をご覧ください。
Radio Group Example Using Roving tabindex | APG | WAI | W3C
ちなみに “Roving” とは「動き回る」「彷徨う」といった意味があるそうです。tabindex が動的に変わることに由来するのかなと想像していますが、実際のところはどうなのでしょうか⋯(詳しい方教えて下さい)
一方 aria-activedescendant はフォーカスされたときに現在アクティブな要素を id で指定し、支援技術に伝えるものです。あくまでも支援技術に情報を伝達するものなので、Roving tabindex と同様にフォーカス時のスタイリングやキーボード操作の実装は別途必要になります。加えて、Roving tabindex で可能だったフォーカス時のスクロール移動の実装も別で必要です。その他 Roving tabindex と異なるのは、画面を拡大するなど input が見えない状態でフォーカス移動しても input の表示位置まで戻って来れるところです。
Radio Group Example Using aria-activedescendant | APG | WAI | W3C
Read This First に書かれているように、モバイルのデバイスやブラウザ、支援技術の組み合わせによってはサポートに差がある可能性があるため動作検証は必須とされています。
どちらを使うかの判断基準
どちらかを使うというよりそもそも併用はできないのか?と思いましたが、以下のような記載から併用するのは適切ではなさそうです。
If a component container has an ARIA role that supports the aria-activedescendant property, it is not necessary to manipulate the tabindex attribute and move DOM focus among focusable elements within the container. Instead, only the container element needs to be included in the tab sequence.
コンポーネント・コンテナがaria-activedescendantプロパティをサポートするARIAロールを持つ場合、tabindex属性を操作してコンテナ内のフォーカス可能な要素間でDOMフォーカスを移動させる必要はありません。 代わりに、コンテナ要素だけがタブシーケンスに含まれる必要があります。(DeepL日本語訳)
Managing Focus in Composites Using aria-activedescendant – Developing a Keyboard Interface | APG | WAI | W3C
ではどちらを使うのが適切かと言われると、結局プロジェクトや要件などに大きく左右されますよね。そのような環境に依存しない部分だと No ARIA is better than Bad ARIA の原則や支援技術のサポート状況は選ぶうえで1つの判断基準にできるかなと思いました。それ以外にもいろいろあると思うので、より良い選択ができるよう環境に依存しない判断基準をもっと養っていきたいです。
まとめ
Roving tabindex や aria-activedescendant が必要になる場面は少ないかもしれませんが、調べてもヒットする記事が少なく情報収集に苦戦したので、この記事が少しでも参考になれば幸いです。
また、フォーカス時にアクティブな項目を伝えるといった挙動は RadioGroup だけでなく、タブ・メニュー・ツリーなどにも求められます。その辺りにも注目してみるとより良い実装に近づきそうだなと思いました。
Gaji-Labo ではアクセシブルな実装に興味があるエンジニアの方とお話しできる機会を求めています…!興味のある方、ぜひカジュアル面談でお話ししましょう!
Gaji-Labo は新規事業やサービス開発に取り組む、事業会社・スタートアップへの支援を行っています。
弊社では、Next.js を用いた Web アプリケーションのフロントエンド開発をリードするフロントエンドエンジニアを募集しています!さまざまなプロダクトやチームに関わりながら、一緒に成長を体験しませんか?
もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください!