はじめに
「アクセシビリティ対応はしたいけれど、キーボード操作やスクリーンリーダー対応まで自前で実装する時間がない」——そんな悩みを抱えたことはないでしょうか。フォーカス管理、ARIA属性の付与、キーボードナビゲーション、スクリーンリーダーへの読み上げ対応など、アクセシブルなUIコンポーネントを正しく作るには非常に多くの知識と検証が必要です。
React Ariaは、この面倒な部分をすべて肩代わりしてくれるライブラリです。見た目のスタイルには一切関与せず、振る舞いとアクセシビリティだけを提供するため、デザインの自由度を保ったまま「動作面で正しいUI」を手に入れられます。
とはいえ、説明を読むより先に触ってみる方が理解は早いはずです。ButtonのonPressやTextFieldのバリデーションが実際にどう動くか、下のサンプルで確認できます。
React Ariaとは
React Ariaは、Adobeが開発・メンテナンスしているオープンソースのUIライブラリで、50種類以上のコンポーネント・フックを提供しています。W3CのARIA Authoring Practices Guideに準拠したセマンティクスを内部で実装しており、マウス・タッチ・キーボード・スクリーンリーダーのすべての操作方法に対応した「world-class accessible」なコンポーネントを、自分のデザインシステムの上に構築できます。
主な特徴
- スタイルフリー設計 - CSSやTailwind、CSS-in-JSなど任意のスタイリング手法と組み合わせ可能で、見た目を制約しません
- 高度なインタラクション対応 - ドラッグ&ドロップ、キーボードでの複数選択、フォームバリデーション、テーブル列のリサイズなど、ネイティブアプリ並みの操作性を実現します
- 国際化対応済み - 30言語以上の翻訳、複数の暦・数値体系、右から左書き言語(RTL)まで標準でサポートしています
- 3段階のカスタマイズレベル - 手軽に使える高レベルコンポーネント、コンテキストを利用した部分カスタマイズ、完全な制御が可能な低レベルHook APIまで、必要に応じて使い分けられます
インストール
もっとも手軽に始められる、コンポーネント形式で提供されるreact-aria-componentsパッケージをインストールします。
# npm
npm install react-aria-components
# yarn
yarn add react-aria-components
# pnpm
pnpm add react-aria-components
より低レベルな制御が必要な場合は、フックのみを提供するreact-ariaパッケージも利用できます。
npm install react-aria react-stately
React Ariaのサンプルを動かす
まずは手を動かして、React AriaのButtonとTextFieldがどう振る舞うかを見てみましょう。下のサンプルは、onPressで押した回数をカウントするボタンと、メールアドレス入力欄を組み合わせたフォームです。TextFieldをvalue・onChangeで制御し、1文字入力するたびに正規表現でメール形式を検証、結果をisInvalidとFieldErrorに反映しています。
要点だけを抜き出すと、次のようになります。
import { useState } from "react";
import { TextField, Label, Input, FieldError, Button } from "react-aria-components";
function App() {
const [email, setEmail] = useState("taro@example.com");
const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
return (
<>
<TextField value={email} onChange={setEmail} isInvalid={!isValid}>
<Label>メールアドレス</Label>
<Input />
<FieldError>{isValid ? "" : "メールアドレスの形式が正しくありません"}</FieldError>
</TextField>
<Button onPress={() => console.log("pressed")}>送信</Button>
</>
);
}
実際に動かせるものが以下です。メールアドレス欄を空にしたり、「@」を含まない文字列に書き換えたりすると、入力のたびにFieldErrorがエラー文言を即座に表示します。ボタンを押した回数もその場でカウントされます。
正規表現のisValid判定をemail.includes("@")のように緩めれば、その分だけエラーが出にくくなるのが分かります。isInvalidはTextFieldだけでなくSelectやNumberFieldなど他のフォームコンポーネントにも共通するプロパティで、React Aria側がaria-invalid属性への反映まで面倒を見てくれます。ボタン側はonPressだけで完結しており、onClickと違ってタッチ操作やキーボードのEnter/Spaceキーでも同じハンドラが呼ばれる点がReact Ariaらしいところです。
基本的な使い方
まずはボタンコンポーネントから試してみましょう。onPressはクリックだけでなく、タッチやキーボード操作にも対応したイベントハンドラです。
import { Button } from "react-aria-components";
function App() {
return (
<Button
className="rounded-md bg-blue-600 px-4 py-2 text-white hover:bg-blue-700"
onPress={() => console.log("clicked!")}
>
送信する
</Button>
);
}
次に、バリデーション付きのフォーム入力を作ってみます。isRequiredを指定するだけで、未入力時のエラー表示までReact Aria側で処理してくれます。
import { Form, TextField, Label, Input, FieldError, Button } from "react-aria-components";
function ContactForm() {
return (
<Form onSubmit={(e) => e.preventDefault()}>
<TextField name="email" type="email" isRequired>
<Label>メールアドレス</Label>
<Input className="border rounded-md px-3 py-2" />
<FieldError className="text-red-600 text-sm" />
</TextField>
<Button type="submit" className="mt-4 rounded-md bg-blue-600 px-4 py-2 text-white">
登録する
</Button>
</Form>
);
}
このコードだけで、Tabキーでのフォーカス移動、スクリーンリーダーへのラベル読み上げ、エラー時のaria-invalid付与などが自動的に行われます。
実践的なユースケース
Selectで作るコレクション系コンポーネント
実務でよく使うセレクトボックスを作成してみましょう。Selectはキーボードでの選択肢移動やタイプアヘッド検索(文字入力で候補を絞り込む機能)にも標準対応しています。ListBoxとListBoxItemで選択肢のコレクションを表現し、Popoverで開閉するドロップダウンを組み立てる構成です。
import {
Select,
SelectValue,
Label,
Button,
Popover,
ListBox,
ListBoxItem,
} from "react-aria-components";
const frameworks = [
{ id: "react", name: "React" },
{ id: "vue", name: "Vue.js" },
{ id: "svelte", name: "Svelte" },
];
function FrameworkSelect() {
return (
<Select className="flex flex-col gap-1">
<Label>使用フレームワーク</Label>
<Button className="border rounded-md px-3 py-2 text-left">
<SelectValue />
</Button>
<Popover className="border rounded-md bg-white shadow-lg">
<ListBox items={frameworks}>
{(item) => (
<ListBoxItem
key={item.id}
id={item.id}
className="px-3 py-2 hover:bg-gray-100 cursor-pointer"
>
{item.name}
</ListBoxItem>
)}
</ListBox>
</Popover>
</Select>
);
}
実際に動かせるものが以下です。矢印キーで選択肢を移動できるほか、フレームワーク名の頭文字(「s」など)を続けて入力すると、その文字から始まる選択肢へタイプアヘッドでジャンプします。選択した値は下に表示されます。
こうしたSelectやComboBox、Table、DatePickerといった複雑な挙動を伴うコンポーネントほど、React Ariaの恩恵は大きくなります。自前で実装すると数百行になりがちなキーボード操作やフォーカストラップのロジックを、意識せずに手に入れられるのが大きな魅力です。frameworks配列に選択肢を追加するだけで、キーボード操作やタイプアヘッドはそのまま新しい項目にも適用されます。
NumberFieldで作る数値系コンポーネント
数量入力やページ数指定のような数値専用フィールドには、NumberFieldが使えます。GroupでInputと増減ボタンをまとめ、Buttonのslot="increment"・slot="decrement"で値の増減を実装します。minValueやmaxValue、stepを指定すれば、範囲外の入力や刻み幅の制御もReact Aria側が担ってくれます。
import { NumberField, Label, Group, Input, Button } from "react-aria-components";
function QuantityField() {
return (
<NumberField defaultValue={1} minValue={1} maxValue={10}>
<Label>数量</Label>
<Group>
<Button slot="decrement">-</Button>
<Input />
<Button slot="increment">+</Button>
</Group>
</NumberField>
);
}
実際に動かせるものが以下です。+・-ボタンのクリックだけでなく、Inputにフォーカスして矢印キーの上下でも値を増減できます。maxValueの10に達するとボタンが自動的に無効化されるのも確認できます。
minValueとmaxValueの範囲を変えれば、上限・下限に達したときのボタン無効化ラインもそのまま変わります。step={0.5}のように刻み幅を指定すれば、小数の数量や単価入力にも応用できます。
デザインシステムを構築中で、既に確立されたスタイルガイドがある場合は、React Ariaのコンポーネントをラップして自社用のコンポーネントライブラリを作るという使い方も相性が良いでしょう。振る舞いはReact Ariaに任せ、見た目だけを自社ブランドに合わせて実装できます。
まとめ
React Ariaは、アクセシビリティ対応という「正しく作るのが難しく、後回しにされがちな部分」をライブラリに委ねられる点が最大の価値です。スタイルには一切干渉しないため、既存のデザインシステムやTailwind CSSとも自然に組み合わせられます。
まずはButtonやTextFieldといったシンプルなコンポーネントから置き換えてみて、SelectやComboBoxなど複雑なUIへと徐々に適用範囲を広げていくのがおすすめです。公式サイトにはコンポーネントごとの詳細なドキュメントとサンプルコードが用意されているので、実装時にはあわせて参照してみてください。