【勉強になる記事紹介】フロントエンドエンジニアが知っておくべきアクセシビリティの基本


[Accessibility essentials every front-end developer should know
（フロントエンドエンジニアが知っておくべきアクセシビリティの基本）](https://martijnhols.nl/blog/accessibility-essentials-every-front-end-developer-should-know#modals) 
という社員に紹介したい記事があったので、要約と私なりの解釈を載せてみました。


この記事では、フロントエンドエンジニアがコンポーネントを構築する際に適用すべき主要なアクセシビリティ原則が説明されています。


トピック
- セマンティックHTML
- フォーム
- キーボード操作
- モーダル
- 画像の代替テキスト
- スタイリング
- ARIA属性

# セマンティックHTML
**セマンティックHTMLを活用することがアクセシビリティの基本**


正しくHTML要素を使うことができれば、ブラウザやツールがページの構造を理解しやすくなります。
加えて、SEOの改善にも役立ちます。

## インタラクティブ要素
**最も重要な要素は、`<button>`と`<a>`で、これらにはキーボード操作やスクリーンリーダーに意味を与えるアクセシビリティ機能が最初から組み込まれています。**

### (Web)アプリケーションにおけるアンチパターン
**`onClick`ハンドラを持つ`div`**

`div`要素にはアクセシビリティ機能がなく、
キーボードでのフォーカスは当たらず、スクリーンリーダでは認識ができません。

故に、キーボード操作可能でクリックしたい要素には、`<button>`と`<a>`を適用するべきです。

### a タグ
Windowsではコントロールクリック、Macではコマンドクリックで**新しいタブとして開けます**。

※ JS で画面遷移すると、前述した挙動ができなくなるので注意が必要です。
`window.location.href = 'パス名';`　みたいなやつです。

### button タグ
インタラクティブな要素なので、デフォルトでキーボード操作が可能です。

※ クリックできるもの、ホバーで見た目に変化がある機能は Button、Linkコンポーネントに集約されていることがベストプラクティスだと私は思っています。

## ネイティブ要素
記事ではネイティブ要素としていますが、[インタラクティブ・コンテンツ](https://www.tagindex.com/html/basic/interactive.html) として区別されているものの一部です。

`<select>`、`<input>`、`<textarea>`などの要素は、追加の実装なしで最初からアクセシブルに設計されています。例えば、`<select> ドロップダウン`は開発者が特別な対応をしなくてもスクリーンリーダーによる読み上げや、キーボード操作が可能です。

**要は可能な限りネイティブHTML要素を使うことで、アクセシビリティを簡単に確保できるという主張です。**

仮に、美的、機能的な理由でカスタムコンポーネントを作りたくなっても、ネイティブHTMLを活用することが推奨されます。どうしても使いたい場合は、[react-select](https://github.com/JedWatson/react-select/tree/master) のようにアクセシビリティがすでにカバーされ、広く使用されているライブラリを使う方がいいようです。

# フォーム
**すべてのフォーム要素は、`<form>` タグ の中に配置し、`onSubmit` イベントハンドラと送信ボタンを設定すべき**

得られる恩恵として以下が挙げられます。
- ブラウザとスクリーンリーダーがフォーム内のフィールドを認識できる
- Enter キーを押すだけでフォームを送信できる
- モバイル端末では、「次へ」ボタンを押すことでキーボードを閉じずに次のフィールドに移動できる

このような基本的なHTML構造に則ると、アクセシビリティと使いやすさが向上します。

## ラベル
**すべての入力フィールドには分かりやすいラベルを付ける。**

ラベルとフォーム要素は正しく関連付ける必要があります。

具体的には：
- フォーム要素には `id` 属性を設定する
- `<label>`には `for` 属性を設定し、フォーム要素の `id` と同じ値を指定する

```html
<label for="email">Email:</label>
<input type="email" id="email" />
```

こうすることで
- スクリーンリーダーは関連ラベルを読み上げるようになるので、視覚障害のあるユーザーがフォームの目的を理解できる
- ラベルをクリックしても対応する入力フィールドが選択されるため、操作性が向上する
- 入力内容の意図が明確になるため、誤入力が減少する（わかりやすいラベルなら）
- 選択範囲が広がるため、モバイルのような小さな画面でもタップしやすくなる
- アクセシビリティ法規に適合する

というように、アクセシビリティ、ユーザビリティ、法的要件を満たすので実装しておくのが無難ですね。

※ 注意
Reactでは、コンポーネントは簡単に再利用でき、同じページで複数回レンダリングされる可能性があるため、ハードコードされたIDの使用は非推奨です。一意なIDを生成するフックである [useId](https://react.dev/reference/react/useId) を使用しましょう。

## プレースホルダー

**プレースホルダーはラベルの代用にはならない**

以下理由
- 入力を始めると見えなくなるので、何のためのフィールドなのか分かりにくくなる
- 入力内容を訂正するフォーム（編集画面など）では全く役に立たない
- 不可視になるとスクリーンリーダーでの読み上げもされなくなる
- コントラストが弱いので視認性が低い
- コントラストを強めすぎると、ユーザーが「入力補完された」と勘違いする可能性がある

placeholder を使用するデメリットは意外にも多いので取り扱いには注意が必要です。

**ベストプラクティスはlabelを活用することです。**


# キーボード操作
- Tabキーで画面上の要素を順番に移動できる
- Enterキーでボタンやリンクを選択・操作できる

など、**キーボードだけでウェブサイトを使えるようにすること**が重要です。

最も簡単で確実なのは、`<button>`や`<a>`などの標準の`HTML`要素を使うことです。
これらの要素は初めからキーボード操作に対応しているため、特別な作り込みを必要としません。

## フォーカスインジケータ
キーボード操作する際、今どの要素が選択されているかを示すフォーカスインジケータが必要です。

例

![画面収録 2025-03-09 9.38.12.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2508607/d45be94f-c7d2-4747-9844-345942aab9b1.gif)


インジケータの見た目が気になる場合は、`:focus` の代わりに `:focus-visible` を使いましょう。
`:focus-visible`は、キーボード操作時のみフォーカスが表示され、マウスでクリックした時にはフォーカスが表示されません。

# モーダル
アクセシブルなモーダルを作るのは手間ですが、`<dialog>` タグを使うと簡単に実装できます。

**モーダルのアクセシビリティ課題**
- フォーカス管理（モーダルを開いたとき、フォーカスをモーダル内に移動させる）
- Tabキーでモーダル内の要素間を移動できる
- 背景コンテンツの非表示（スクリーンリーダーが背景を読まないようにする）

**解決策**
**最も簡単な方法は、HTML標準の `<dialog>` 要素を使う** ことで、複雑な `JavaScript` を書かなくても、アクセシブルなモーダルを簡単に実装できます。

## 確認ダイアログ
確認ダイアログが表示されたとき、自動的に「確認」または「OK」ボタンにフォーカスを設定するようにしてもいいかもしれません。

通常のモーダルでは、「閉じる」ボタンや最初の入力フィールドに初期フォーカスを当てますが、確認ダイアログでは、主要なアクションボタンに初期フォーカスを当てることですぐにEnterキーで確定できます。

**ただし、「削除系」など破壊的な操作の場合は、安全のために「キャンセル」ボタンに初期フォーカスを設定する場合もあると思います。**

## カスタムモーダル
仮にカスタムモーダルを `<dialog>` を使わずに作る場合、アクセシビリティに注意しなければなりません。

### フォーカス管理
モーダルが開いた後、フォーカスが「モーダルを開く」ボタンに留まったままだと、
同じモーダルが繰り返し開かれてしまうかもしれません。

これを防ぐには以下のような対応が必要です
- モーダルが開いたらフォーカスをモーダル内の要素に移動する
- ユーザーが背景のページへタブ移動できないようにする
- モーダルが閉じたときにフォーカスを元の要素に戻す

この課題について、[react-focus-lock](https://github.com/theKashey/react-focus-lock) のようなライブラリが良いとされています。

### 背景を非アクティブにする
モーダルが開くと、その背後にあるコンテンツは背景によってブロックされます。ただし、スクリーンリーダーを使用しているユーザーは、背後のコンテンツを操作できるかもしれません。

対応方法として、背後のコンテンツに[inert属性](https://developer.mozilla.org/ja/docs/Web/API/HTMLElement/inert)を追加することで、コンテンツが非インタラクティブになり、支援技術から隠されます。

### React でinertを使うときの注意点
モーダルをコンポーネントの中に書くと、
**親要素に「inert」を設定したときにモーダルも不活性になってしまいます。**

これを解決するには **「ポータル」** を使います
ポータルとは、モーダルのコードを書く場所と、実際に表示される場所を分ける技術です

[createPortal(children, domNode, key?)](https://ja.react.dev/reference/react-dom/createPortal) により、
モーダルを「inert」の影響範囲の外に配置できます。

以下は公式ドキュメントから拝借した例です。
`createPortal` に渡したnodeをbody直下に配置しています。

```jsx
<div>
  <p>This child is placed in the parent div.</p>
  {createPortal(
    <p>This child is placed in the document body.</p>,
    document.body
  )}
</div>
```

※ UIライブラリなどを使用すると、「レンダーされるモーダルがDOMの変な位置にレンダーされているなー」と思ったらこやつの仕業の可能性があります。

### モーダルを閉じる
`Escape` キーでモーダルを閉じることができるようにする。
これはドロップダウンメニューを閉じる時でも考慮されるパターンです。


# 画像の代替テキスト alt
`alt` テキストは画像をアクセシブルにする必須要素です。
SEOの観点からも alt を書いておくことが推奨されています。

※ [TestingLibrary](https://testing-library.com/docs/queries/byrole/) では、 `alt` 属性がないと`role`取得できないことから「altがないものはimgにあらず」というメッセージを暗に発しています。

ただ、純粋に装飾目的な画像なら、空のaltテキスト(`alt=""`)を使用します。
これにより、スクリーンリーダーは画像を認識しなくなります。

## alt テキストを書く
alt テキストはどのように書くといいのでしょうか？

視力障害を持った方は画像の一部しか見ることができません。
代替テキストは、彼らが見えているものと見えていないものとのギャップを埋めるような説明であるべきです。

例えば、 **画像にテキストが含まれている場合、そのテキストはaltテキストにも含まれるべきです。**


# スタイリング
デザインはアクセシビリティと強い関連があります。

例えば
- フォーカスされた要素を枠線などで強調表示する
- リンクはリンクらしく、ボタンはボタンらしく見せる
- ホバー状態、アクティブ状態、無効状態を明確に示す
- 十分なコントラストを保つ
- 色覚異常のあるユーザーのために、色だけでなくテキストやアイコンも組み合わせる
- カスタムフォントサイズやズーム機能をサポートする
- 動きに敏感なユーザーのために、アニメーションを減らすか無効にできるようにする
- 読字障害のあるユーザーのために、明確なフォントと適切な間隔を使用する

これらはデザイナーの問題でありフロントエンドエンジニアの影響範囲外かもしれませんが、できる範囲で取り組みたい課題です。
時には、デザイナーに警鐘を鳴らすことも重要だと思っています。

## クリック可能な領域
マウスやタッチ操作をする人がクリックしやすいように、ボタンやリンクは十分なパディングを持たせましょう。

下段の画像がホバーした時のデザインで、クリックできる範囲を十分にとっています。

<img width='400' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2508607/4b4631b4-3d4d-47c2-a937-776544afc476.png' />


## アニメーションの動きを調整できる

ページ遷移、モーダルの表示、状態の切り替えなどでアニメーションがあるとアプリケーションが使いやすくなる場合もあります。

**ただ、一部のユーザーにとっては、アニメーションで不快感や体調不良を感じることがあります。**
そのようなユーザーは、OSの設定で「動きを減らす」モードをオンにしていることがあります。

例えば、MacOSでは以下から設定できます。
「システム設定 > アクセシビリティ > ディスプレイ > 視差効果を減らす」
[prefers-reduced-motion](https://developer.mozilla.org/ja/docs/Web/CSS/@media/prefers-reduced-motion) でデモが見えるので、そこで確認すると分かりやすいです。


**ユーザーのOS設定で「Reduce Motion」が選択されている場合、アニメーションを無効にした方が良いです。**

これはCSSのメディアクエリを使って簡単に実装できます。

```css
.animation {
  animation: pulse 1s linear infinite both;
}

/* Tone down the animation to avoid vestibular motion triggers like scaling or panning large objects. */
@media (prefers-reduced-motion) {
  .animation {
    animation-name: dissolve;
  }
}
```

このアニメーションは Reduce Motion を有効にしている場合に抑制されます。



## アクセシブルなレスポンスデザイン
**レイアウト値の指定には `px` ではなく `rem`, `em` を使用する**

ブラウザではウェブページのデフォルトフォントサイズをユーザーがカスタマイズできるようになっています。

例えば、chromeでは
「設定 > デザイン > フォントサイズ」で 小、中、大 と選択できるようになっています。

アクセシブルなレスポンシブデザインでは、**ユーザーのフォントサイズ設定やズームレベルに合わせてレイアウトも変化させる方が良いです。**

やり方としては、フォントサイズ、余白などの **レイアウト値に絶対値である `px` を使用するのを避け、`em` や `rem` などの相対単位を使用する** ことです。

仮に `px` を使用していると絶対値指定なため、ブラウザのフォントサイズ設定を変更しても、それが反映されません。

# ARIA属性
[HTML解体新書](https://www.amazon.co.jp/HTML%E8%A7%A3%E4%BD%93%E6%96%B0%E6%9B%B8-%E4%BB%95%E6%A7%98%E3%81%8B%E3%82%89%E7%B4%90%E8%A7%A3%E3%81%8F%E6%9C%AC%E6%A0%BC%E5%85%A5%E9%96%80-%E5%A4%AA%E7%94%B0-%E8%89%AF%E5%85%B8/dp/4862465277) (p.932) の格言が印象的なので掲載します
>ARIAを使う際にもっとも注意すべきことは、ARIAを使わないようにすることです

ARIAの誤用は、使わないよりも害を及ぼす可能性があるため、必要最小限に使用するのがベストだそうです。

大抵の場合、適切なセマンティックHTML要素（`<button>`、`<nav>`、`<header>`など）を使用するだけで十分であり、ARIA属性を追加しなくても良いです。ARIAは既存のセマンティクスを補完するものであり、置き換えるものではありません。

ARIAでも重要な属性は `aria-label` と `aria-hidden` だそうです。

## aria-label
テキストが表示されていない要素にアクセシブルなラベルを追加できます。
例えば、アイコンだけの検索ボタンには、`aria-label` を含める必要があります。

```tsx
<button aria-label="検索">
  <SearchIcon />
</button>
```
上記の場合では、スクリーンリーダーで「検索」と読み上げられるようになります。

`aria-label`はどの要素でも使用できますが、**インタラクティブな要素でのみ使用します**。
非インタラクティブな要素で使用すると、ラベルが無視されたり、混乱を招く不要な読み上げをもたらすことがあるからです。

## aria-hidden
`aria-hidden="true"`を記述することで視覚的には表示されるが、スクリーンリーダーには読み上げられなくなります。

主に装飾目的で、それ自体に意味のない要素に付加されます。

```tsx
<div>
  React <ReactLogo aria-hidden />
</div>
```


前述した二つの要素はスクリーンリーダーサポートの出発点で、フルサポートには、`aria-live`、`aria-expanded`、`aria-describedby`などのARIA属性が必要になってきます。

# まとめ
今まで紹介してきたものは、フロントエンドエンジニアがコンポーネントを作成する際に注意すべき原則です。**アクセシビリティは後で取り組む別のタスクではなく、最初から開発過程の一部である必要があります。**

アクセシビリティの改善は、大抵の場合、特定のニーズを持つユーザーだけでなく、誰にとっても使いやすいアプリケーションの構築に役立ちます。また、検索エンジンがアクセシビリティについて考慮されたサイトを高く評価するため、SEO的にも有利に働きます。

今まで紹介してきたことはアクセシビリティの基本であり、出発点に過ぎません。
本当に誰もが使えるアプリを作るにはさらなる配慮・取り組みが必要です。

最終的には、**実際にスクリーンリーダーを使ったテストすることが重要です。**


# 感想
アクセシビリティに配慮したコンポーネント設計をする際、
「これくらい知っておいた方がいいよね」のいいラインについて紹介だと思います。

「アニメーションを低減させる」など知らないこともあったので大変勉強になりました。

# 補足・注意
[Accessibility essentials every front-end developer should know](https://martijnhols.nl/blog/accessibility-essentials-every-front-end-developer-should-know#modals) 
をそのまま日本語訳にしたものではありません。

もし気になる方は上記の記事を読むことをおすすめします。

# 参考
- [Accessibility essentials every front-end developer should know](https://martijnhols.nl/blog/accessibility-essentials-every-front-end-developer-should-know#modals) 
- [インタラクティブ・コンテンツについて](https://www.tagindex.com/html/basic/interactive.html)
- [HTML解体新書](https://www.amazon.co.jp/HTML%E8%A7%A3%E4%BD%93%E6%96%B0%E6%9B%B8-%E4%BB%95%E6%A7%98%E3%81%8B%E3%82%89%E7%B4%90%E8%A7%A3%E3%81%8F%E6%9C%AC%E6%A0%BC%E5%85%A5%E9%96%80-%E5%A4%AA%E7%94%B0-%E8%89%AF%E5%85%B8/dp/4862465277)