はじめに
react-testing-library を使ってテストを書いていると、そもそもテスト対象の Accessibility に問題があり、getByRole()
でテスト対象を取得できないということに遭遇することがよくあります。そんなことから、Webアクセスビリティについて真面目に学びたいと思い、Webアプリケーションアクセシビリティ──今日から始める現場からの改善を読んでいます。その中で紹介されていた React Aria について、簡単に説明します。
React Aria とはなにか?
Adobe のデザインシステムである Spectrum を構成するライブラリの一つ。UIコンポーネントのアクセシビリティを実現するうえで必要となる共通処理をHookとして提供するもの。aria属性だけでなく、キーボードナビゲーションやモバイルデバイスでの操作制御なども組み込まれている。
なにができるのか?
React Aria を使うと何ができるのかを理解するには、useButton のドキュメントからリンクされている、次の記事を読むとよい。
以下、要約。
- ボタンの実装は簡単に見えて、しかしマルチデバイスについて考えると考慮しなければならないことがたくさんある
- タッチデバイスにおいて、ブラウザはタッチ操作を可能な限りマウス操作としてエミュレートするよう努めるが、それでも完全な再現はできない。例えば、ボタンをタップして、そのままスライドしてから話すと、
:active
と:hover
スタイルが残ってしまうなど。 - Pointer Event を使うことで、ポインターデバイスのイベントを抽象化できるが、ブラウザによって若干動きが異なる場合がある。
- ボタン押下中に電話を受けた時など、タッチ操作がキャンセルされる場合がある。
- iOS において、ボタンを押下 -> スライドして選択開始すると、ボタンのラベルが選択されない。
- キーを押下し続けると onKeyDown イベントがリピートされるが、これが不都合になるケースもある。例えば、ボタンを押下するとメニューが表示され、先頭のメニューにフォーカスが移るようなケース。
React Aria はこのような問題を解決することを目的としている。
useButton の動きを観察してみる
useButton はonClicked
などに替わって onPressXXX
系のイベントをハンドルして制御することになる。次のようなサンプルコードを書いて、動きを観察してみる。
const Button = (props: Parameters<typeof useButton>[0]) => {
const ref = useRef<HTMLButtonElement>(null);
const { buttonProps, isPressed } = useButton(
{
onPress: console.log,
onPressStart: console.log,
onPressEnd: console.log,
onPressUp: console.log,
...props,
},
ref
);
return (
<button {...buttonProps} ref={ref}>
{isPressed ? "Pressed" : "Not pressed"}
</button>
);
};
Chrome で操作してみた結果がこちら。
マウスでボタンを押下したまま動かしています。マウスがボタン上から外れると isPressed=false
になり、再度ボタン上にHoverすると isPressed=true
になっているのがわかる。
これと同様の操作をタッチデバイスで行ったのがこちら。
ボタン上からポインターが一度外れると、再度Hoverしても isPressed=false
のままになり、上下スライドで画面がスクロールする。
また、ボタン押下中は user-select: none
が付与され、テキスト選択を無効化してくれている。
最後に
アクセシビリティを勉強していると、考えるべきことが非常に多く、また実現するための唯一の正解があるわけでもなく、多大な知識と労力が必要になると感じています。そんな中で、React Aria はアクセシビリティ改善の大きな助けとなりそうです。(ただ、MITではなくApache2ライセンスなので注意)
今回は時間がなかったので、本当にさわり部分だけの紹介になってしまいましたが、状態管理ライブラリである React Stately も合わせて、今後理解を深めていきたいと思います。
以上。