StorybookのRealtime accessibility testsを使ってみよう

はじめに
今回は、Storybook8.5で追加されたRealtime accessibility testsについて紹介してみたいと思います!
Accessibilityとは
Accessibilityについてご存知でしょうか?Accessibility(アクセシビリティ)は、視覚障害を持つユーザーを含むすべての人が正しく操作できるようにするための仕組みです。例えば、WAI-ARIAは、HTMLでは表現できないUIの状態を補完し、より正しくWebアプリケーションの状態をスクリーンリーダーに伝えることができます。これにより、より多くのユーザーが快適にWebアプリケーションを利用することができます。
アクセシビリティを適切に設定することの重要性は増しており、アメリカではアクセシビリティが不十分な場合、訴訟の対象となることがあります。日本でも、デジタル庁がアクセシビリティ導入に関するガイドラインを整備しています。このような背景から、現代においてはアクセシビリティに配慮したアプリケーション開発の重要性はますます高まっています。
アクセシビリティを正しく設定するには、HTMLへの理解やWAI-ARIAなどの専門知識が必要ですが、Storybookを使うことで比較的簡単にアクセシビリティの観点を開発に組み込めます。Storybookは、UIコンポーネントを個別に開発・テストするためのツールであり、アドオンを追加することで様々な拡張機能が使うことができます。今回はアクセシビリティのアドオンを追加し、コンポーネント単位でのアクセシビリティ観点の確認を効率化してみましょう。
準備
今回はbun+react+viteの環境下でStorybookを動かしてみます。
% bun --version
1.1.42
プロジェクトの作成
% bun create vite
│
◇ Project name:
│ storybook-a11y-test
│
◇ Select a framework:
│ React
│
◇ Select a variant:
│ TypeScript + SWC
│
◇ Scaffolding project in /Users/rhirata/Documents/repo/storybook-a11y-test...
│
└ Done. Now run:
cd storybook-a11y-test
bun install
bun run dev
起動してみます。
cd storybook-a11y-test
bun install
bun run dev
下記の画面が表示されればOKです。

Storybook導入
Storybookをインストールします。今回はver 8.5を使います。
% bunx storybook@8.5 init
起動してみます。
% bun run storybook
localhost:6006にアクセスし、下記のようにStorybookが表示されればOKです。

@storybook/addon-a11y追加
アクセシビリティのStorybookアドオンを追加します。ちなみにAccessibilityはaとyの間にアルファベットが11文字あるためa11yと略される場合があります。Storybookのアドオンもa11y表記ですね。
bunx storybook add @storybook/addon-a11y@8.5
Buttonの検査項目について確認する
Storybookはインストールされた際にサンプルのコンポーネントがいくつか追加されます。今回はその中の一つであるButtonコンポーネントについて、アクセシビリティの検査項目について確認してみましょう。
Storyの一覧からButton→Primaryを選択し、画面下部にAccessibilityタブがあるので選択します。Buttonコンポーネントの検査項目は5つあり、すべてが合格しているのがわかると思います。

それぞれの検査項目について内容を確認しましょう。
aria-hidden="true" must not be present on the document body
こちらはbodyタグにaria-hidden="true"が設定されていると違反になります。aria-hidden="true"をbodyに設定するとドキュメント全体がスクリーンリーダが参照できなくなってしまいます。
ARIA hidden element must not be focusable or contain focusable elements
aria-hidden="true"が設定された要素がフォーカス可能、またはフォーカス可能な子要素を持つ場合違反となります。aria-hiddenは視覚的に表示されているが支援技術から隠したい要素にのみ使用する必要があります。
Buttons must have discernible text
button要素が識別可能なテキストを持たない場合違反となります。スクリーンリーダで読み上げる際にテキストが設定されている必要があります。アイコンのボタンの場合はaria-labelでテキストを提供する必要があります。
Elements must meet minimum color contrast ratio thresholds
背景色と前景色のコントラスト比が基準値を満たしていない場合は違反となります。適切なコントラストは視覚障害を持つユーザや、明るい環境でのデバイス使用のために重要です。
Interactive controls must not be nested
インタラクションを含む要素がネストされている場合違反となります。例えばbutton要素内にaタグがある等が考えられます。このような場合、スクリーンリーダが正しく読み上げられない・キーボード操作時のフォーカス順序が不明確になる等の問題が発生する可能性があります。
Storyの追加
StoryのデフォルトのButtonは全てのチェックに合格しています。そこで、あえてチェックに失敗するようなコンポーネントを作成し、StorybookのAccessibilityタブを確認してみましょう。
body要素は今回は使用しないので、それ以外のButtonのチェックが失敗するようなコンポーネントBadButtonを作ります。
import './button.css';
import { ButtonProps } from './Button';
export const BadButton = ({
primary = false,
size = 'medium',
label,
...props
}: ButtonProps) => {
const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
return (
<button
type="button"
className={['storybook-button', storybook-button--${size}
, mode].join(' ')}
style={{
backgroundColor: 'pink',
}}
{...props}
>
<a href="/" tabIndex={0} style={{ color: 'red' }} aria-hidden="true">
{label}
</a>
</button>
);
};
問題点は下記のとおりです。
- aタグにaria-hiddenを指定しているがフォーカスしてしまう
- コントラスト比が基準値を満たしていない
- button要素がテキストを持たない(aタグはaria-hiddenを設定しているため)
- button要素がaタグをネストしている
Storyも作ります。
import type { Meta, StoryObj } from '@storybook/react';
import { fn } from '@storybook/test';
import { BadButton } from './BadButton';
const meta = {
title: 'Example/BadButton',
component: BadButton,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {
backgroundColor: { control: 'color' },
},
args: { onClick: fn() },
} satisfies Meta<typeof BadButton>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Primary: Story = {
args: {
primary: true,
label: 'Button',
},
};
テストを確認
BadButtonのストーリーを開き、Accessibilityタブでテスト結果を確認しましょう。

意図した通りに違反が検出されていたらOKです!余力があれば上記の検査項目の解説を参考に違反を解消してみましょう。
おわりに
今回はStorybookのRealtime accessibility testを使って手軽にコンポーネントのアクセシビリティの対応状況をチェックできることを紹介させていただきました。みなさまのアクセシビリティ対応の第一歩として本記事が役立てば幸いです。それでは!
参考
https://storybook.js.org/blog/storybook-8-5/
https://www.digital.go.jp/resources/introduction-to-web-accessibility-guidebook