# 今すぐできる Web アクセシビリティ改善

アクセシビリティの対応は、要件定義段階から設計したりなど大変そうなイメージはあります。実際に本格的に対応するにはどの達成基準を目標にするか決めたり、入念なテストが必要になるなど確かに一朝一夕で対応できるわけではないでしょう。

ですが、完璧な対応を目指すのではなく、少しづづ改善できるところから進めることもできます。実際にやってみると、簡単な HTML や CSS のコードの修正でもよい改善を得られたりします。

この記事では比較的低コストで実践できるアクセシビリティの改善を紹介します。

## `<div>` や `<span>` に onClick しない

はじめは、`<div>` や `<span>` タグに `onClick` をしないでという話です。もしあなたが携わっているプロダクトにこのようなコードがあったら今すぐ `<button>` タグに変更しましょう。

### 理由
`<div>` や `<span>` に `onClick` してはいけない理由は以下の 2 点です。

- キーボードで操作できない
- クリック可能な要素だと認識できない

#### キーボード操作できない
まずはキーボードで操作できないという観点についてです。第 1 に、`<div>` や `<span>` タグは `Tab` キーを入力したときフォーカスできません。フォーカスできない要素にはキーボードによる操作からはたどり着くことができません。

`Tab` キーを入力したときにフォーカス可能である要素は [逐次フォーカス可能](https://momdo.github.io/html/interaction.html#sequentially-focusable) と整理されています。`<input>` や `<button>` などのフォームコントロール、`<a>` のようなリンクは基本的には逐次フォーカス可能です。`<div>` など逐次フォーカス可能ではない要素に対しても `tabIndex` に 0 以上の値を指定することで逐次フォーカス可能でとなります。

仮に `<div>` や `<span>` タグにフォーカスできるようになったとしても、まだキーボードで操作できません。`onClick` イベントは通常マウスで左クリックしたときに発火するものだからです。

`<button>` タグを使用している場合には、フォーカスしている際に `Enter` キーまたは `Scape` キーを入力することで `onClick` イベントを発火させることができます。

#### クリック可能な要素だと認識できない
続いてクリック可能な要素だと認識できないという観点についてです。この観点は主にスクリーンリーダーなど支援技術を利用しているユーザーに対しての話になります。

`<div>` や `<span>` タグを使っている場合には、スクリーンリーダーからは単なるテキストだと認識されます。外見上でいかにも押すことができそうなボタンに装飾でされていたとしても、スクリーンリーダにボタンだと認識されなければ意味がありません。

スクリーンリーダーが対象の要素がどのような種類なのか判断するためには、ロールという概念が使われます。ロールとはその要素がどのような役割を果たすか識別するためのものです。例えば `button` ロールはユーザーからの操作によって何かアクションを起こすもの、という役割が、`textbox` ロールはテキストを入力できる要素であるという役割が期待されています。

HTML の要素には暗黙的にロールが定義されています。HTML 要素にはあらかじめ定められた役割が定義されているということです。下記はほんの一例です。

| HTML 要素     | 暗黙のロール     |
| ---------- | ---------- |
| `<button>`       | [button ロール](https://developer.mozilla.org/ja/docs/Web/Accessibility/ARIA/Roles/button_role)       |
| `href` 属性を持つ `<a>`       | [link ロール](https://developer.mozilla.org/ja/docs/Web/Accessibility/ARIA/Roles/link_role)       |
| `<input type="text" />`       | [textbox ロール](https://developer.mozilla.org/ja/docs/Web/Accessibility/ARIA/Roles/textbox_role)       |
| `<ul>`       | [list ロール](https://developer.mozilla.org/ja/docs/Web/Accessibility/ARIA/Roles/List_role)       |
| `<li>`       | [listite ロール](https://developer.mozilla.org/ja/docs/Web/Accessibility/ARIA/Roles/Listitem_role) | 

適切なロールを持った要素を適切な箇所で使うことが大切です。

`button` ロールを持つ要素は大抵のスクリーンリーダーで「${ボタン内のテキスト} ボタン」のように読み上げられます。その後現在ボタン上にいて、ボタンをクリックできるということが読み上げられます。

![ブラウザの「Click Me」と表示されているボタンにフォーカスがあたっている。Voice Over の吹き出しが表示されていて、「現在、Web コンテンツ内のボタン上にいます。このボタンをクリックするには、Control-Option-スペースを押します。この Web 領域を終了するには、Control-Option-」と記載されている。](//images.ctfassets.net/in6v9lxmm5c8/1s2aekN4gUF0BmdwZLDS3C/ccbfb260e5b43997cddf262001424241/5cdc245e762847ab90caed0410ad3839.png)

以上のことより、`onClick` がついていてクリック可能な要素には大抵 `<button>` を使っておけばいいことがわかります。

### `<button>` に変更するときの注意
`<div>` や `<span>` タグをそのまま `<button>` に変更すると、思わぬ変化が起きてしまう可能性があるので注意が必要です。まず `<button>` に変更するとブラウザのデフォルトのスタイルが適用されるので、見た目が大きく崩れてしまいます。以下のようにデフォルトのスタイルを打ち消す CSS を適用させて置くといいでしょう。

```css
.style-reset-button {
   background: none;
   border: none;
   outline: none;
   appearance: none;
   padding: 0;
   cursor: pointer;
   text-align: left;
   color: inherit;
 }
```

2 つ目に `type="button"` は常に設定しておくとよいです。`<form>` 内にあり `type` が指定されていないボタンはすべてサブミットボタンとして扱われます。ボタンをクリックすると勝手にフォームを送信してしまうなんてことになりがちです。eslint で `<button>` の `type` 属性を強制するルールもあるので、これを適用させておくと安全です。[eslint-plugin-react/button-has-type.md at master · jsx-eslint/eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/button-has-type.md)

## tabIndex に正の値をつけない

tabIndex には正の値をつけてはいけません。`tabIndex` に 1 以上の値を設定している箇所があったら今すぐ消しましょう。`tabIndex` に指定していいいのは `0` と `-1` だけです。

### 理由
tabIndex に正の値をつけてはいけない理由は、キーボード操作によるフォーカスの順序がめちゃくちゃになるからです。フォーカスは HTML の文章構造に従って、左上から右下に移動することが期待されています。フォーカス順序が正しくないと、ユーザーの混乱を招いてしまいます。

フォーカス順序については、`position: absolute;` や `flex-direction: row-reverse;` のようなスタイルを使用している場合にも見た目上のフォーカス順序にならない恐れがあるので注意が必要です。

## フォーカスを見せる

続いてフォーカスを見せるという観点です。みなさんの CSS には以下のようなスタイルがないでしょうか？

```css
 * {
  outline: 0;
}
```

`outline: 0;` を指定するのをやめるか、キーボード操作でフォーカスしたことがわかるような代替の手段が必要です。

### 理由

ブラウザのデフォルトのスタイルでは、ボタンやリンクに対してフォーカスした際にアウトラインが表示されるようになっています。1 つ目のボタンに今フォーカスしているのですが、青い枠線が表示されているのがわかるかと思います。これはフォーカスリングと呼ばれています。

![ブラウザ上にボタンが3つ並んでいる。そのうち1つ目のボタンにフォーカスしていて、青い枠線が表示されている・](//images.ctfassets.net/in6v9lxmm5c8/6Y7mjJmINV9tw9Atass3oo/88781512b8223d7f6f0824f5753cba94/df26e8177de745dd9aea746b17bd5abd.png)

`outline: 0` を指定してみるとどうでしょう。現在どのボタンに対してフォーカスがあたっているのかわからなくなってしまいました。フォーカスリングを頼りに現在位置を把握しているキーボードで操作しているユーザーにとっては、マウスポインタが表示されなくなるのに匹敵するくらいのヤバさです。

![ブラウザ上にボタンが3つ並んでいる。青い枠線が表示されないのでどのボタンに対してフォーカスしているのかわからない。](//images.ctfassets.net/in6v9lxmm5c8/011ccfZ5Gp6vNhKxdDRJGy/0580a0cca51fbbffa34b128d39794a06/f33d9f9278e94f8b89771cc004bfd17e.png)

`outline: 0;` を指定するのなら代替手段が必要です。

フォーカスしたときにフォーカスリングを表示するスタイルを記述したいところなのですが。`:focus` 擬似クラスを使うとキーボード操作でフォーカスしたときだけではなく、マウス操作でフォーカスしたときにもスタイルがで起用されます。

マウスでクリックしたときにもフォーカスリングが表示されるのはデザイン上受け入れられない、ということもあるかと思います。

そのような場合には `focus-visible` 擬似クラスを使うと良いです。この擬似クラスは、マウスクリック動作でフォーカスしたときには適用されず、キーボード操作でフォーカスしたときのみ適用されます。

> :focus-visible 擬似クラスは、要素が :focus 擬似クラスに一致しているときで、ユーザーエージェントが要素にフォーカスを明示するべきであると推測的に判断した場合に適用されます (多くのブラウザーではこの場合、既定で「フォーカスリング」を表示します)。

### フォーカスの視認性

その他、フォーカスしたときに表示されるアウトラインは十分に視認できるようにする必要があります。例えば、ボタンの背景色がブラウザのデフォルトのフォーカスリングの色と同一ですと認識しづらいです。コンストラクタ比を意識したアウトラインの色を設定する必要があるでしょう。

またフォーカスリングはだいたい 2px くらいの太さが必要だと言われています。デザイン上通常時にはボーダーを細くしておきた場合には、 `box-shadow` で 2px 以上の太さとなるフォーカスリングを表現する方法も有効です。

## 名前をつける

最後に、要素には適切な名前をつけようという観点です。スクリーンリーダーを利用するユーザーによっては、要素を識別するために適切な名前が付けられていることが重要です。スクリーンリーダーが要素を読み上げる際に使われる名前は「アクセシブルな名前」と呼ばれます。

入力フォームを例にとって見てみましょう。視覚的には名前入力するためのフォームだと認識できるのですが、アクセシブル名前が存在しない場合「テキストを編集」としか読み上げられないので、どの項目に対する入力フォームなのか判別できなくなってしまいます。

![ブラウザ上で名前、入力フォームの順番に縦に並んでおり、入力フォームにフォーカスが当たっている。Voice Overの吹き出しが表示されていて、「テキストを編集、カラ」と記述されている。](//images.ctfassets.net/in6v9lxmm5c8/1ZjewuWg3O6GjG5GWZw9UK/b3063b85e64757fcbd7b2daf5c307ad0/eaf35a56f81045a28f5c981e29353ffc.png)

正しくアクセシブルな名前がついている場合には「名前、テキストを編集」のように読み上げられます。

![ブラウザ上で名前、入力フォームの順番に縦に並んでおり、入力フォームにフォーカスが当たっている。Voice Overの吹き出しが表示されていて、「名前、テキストを編集」と記述されている。](//images.ctfassets.net/in6v9lxmm5c8/3UJrd5C02G3FDsx7U4xkqs/22b8be30041d8b1bf039a14acf6b18a4/ecf40f66e8a84725b50583207b65b470.png)

ここでは、さまざまな要素に対して適切にアクションな名前をつける方法を見ていきましょう。

### 入力フォーム

まずは入力フォームです。入力フォームに対しては `<label>` 要素を使用してアクセシブルな名前をつけるのがベストです。`<label>` 要素を使って入力フォームとテキストを関連付けておくと、ただスクリーンリーダーで読み上げてくれるだけでなく、クリック可能な範囲も広がるので良い事づくしです。

ところで、`<label>` 要素をテキストと入力フォームを関連付ける方法には、`for` 属性を使う方法と `<label>` 要素でラップする 2 つの方法があります。前者の方法は明示的なラベルと呼ばれていて、ラベルと 1 対 1 のマッピングが提供されます。この方法はすべてのモダンブラウザと収容な支援技術にサポートされているため、こちらの方法を使うのが良いでしょう。

```html:明示的なラベル
<label for="name">名前</label>
<input id="name" type="text" />
```

後者は暗示的なラベルと呼ばれます。この方法は支援技術によるサポートに一貫性がありません。

```html:暗示的なラベル
<label>
 名前
 <input type="text" />
</label>
```

### 画像

画像については alt 属性を設定してください。画像に alt 属性を指定したほうがいい、というのは聞いたことがある人も多いのではないでしょうか？`alt` 属性を指定すると、スクリーンリーダーに読み上げられるだけでなく、画像の読み込みに失敗した際にも代替テキストとして表示してくれるので便利です。

`alt` 属性には、ある画像を見てない人でも想像できるように、写真の内容を口頭で説明するようなテキストを書くと良いでしょう。

例えば以下の画像を見てください。

![白い猫がこちらを見上げている](//images.ctfassets.net/in6v9lxmm5c8/6ZWdfMSy3YQQtZmep1J26u/8f4115d49eec2f19a077ee4911bdf429/_1__1_.jpeg)

この画像に対して `alt="猫"` のような表現ですと、実際の画像に近い光景を想像できません。`alt="白い猫がこちらを見上げている"` くらいのテキストを入れてあげると良いでしょう。

また装飾目的の画像には空文字の `alt` 属性を指定するようにします。`alt` 属性自体を指定しなかったりすると、ファイル名などが読み上げられてしまいます。読み上げをさせないという意図を示すために必ず　`alt` 属性を指定する必要があります。

### アイコンボタン
アイコンをクリックしたときになにかアクションを起こすことはよくあります。例えば、ダイアログの閉じるボタンなどです。アイコンは基本的にテキストを持っていないので、アクセシブルな名前が存在しません。

![Twitter のツイート投稿ダイアログ。閉じるボタンが赤丸で強調されている。](//images.ctfassets.net/in6v9lxmm5c8/5lBUlSzAC3i9ApURHKz23f/e56ea31f02e4ff70e8e95a27f9738677/9eecd0f8e7e84e129995808a60562ce6.png)

アクセシブルな名前が存在しないボタンは、単に「ボタン」と読み上げられるので、クリックしてみるまで何が起きるか予測できません。

このような状況の解決方法は主に 2 つあります。1 つ目は `aria-label` 属性を使用することです。`aria-label` 属性はアクセシブルな名前を与えるための属性です。`aria-label` で指定したテキストはアクセシブルな名前として優先的に使用されます。

以下のように、`<button>` 要素に対して `aria-label` で閉じるを指定すると「閉じる、ボタン」のように読み上げられます。これにより、スクリーンリーダーユーザーにとってもクリックしたら何かを閉じるボタンだと認識できます。

```html
<button type="button" aria-label="閉じる">
  <i class="fas fa-times" aria-hidden="true" />
　</button>
```

![ブラウザ上のボタンにフォーカスしている。Voice Over で吹き出しが表示されていて、「閉じる、ボタン」と記載されている。](//images.ctfassets.net/in6v9lxmm5c8/2evXoESAA3AYYNsOWFay98/6dce0d0b8f12530a7dcb362885830ea6/ed5bf28a0e414cffbdfc0d0e76fff8fa.png)

`aria-label` を使うときには、他によりよい手段がないかどうか考えてからにしてください。例えば、さきほど挙げた入力フォームのアクセシブルな名前も `aria-label` でつけることもできます、しかし、`aria-label` を使うようは標準的な `<label>` 要素を使うほうが好ましいです。なぜなら、`aria-label` はスクリーンリーダーユーザーからは確認できますが、視覚的アプリケーションを使用するユーザーからは確認できないためです。

2 つ目の方法としては、不可視のテキストを使う方法です。これは CSS のスタイルでテキストを視覚的には見えなくようにして、スクリーンリーダーにのみ読み上げられるようにする方法です。`bootstrap` や `TailwindCSS` などの CSS フレームワークもこのようなスクリーンリーダーにのみ表示されるユーティリティクラスを提供しています。

```html
<style>
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}
</style>

<button type="button">
  <div class="sr-only">閉じる</div>
  <i class="fas fa-times" aria-hidden="true" />
</button>
```

`display: none` のようなスタイルでも視覚的に非表示にできますが、同時にスクリーンリーダーからも読み上げられなくなってしまうので、使うことができません。

## 参考

- [Accessibility tree (アクセシビリティツリー) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN](https://developer.mozilla.org/ja/docs/Glossary/Accessibility_tree)
- [ボタンは button で実装しよう](https://qiita.com/sukoyakarizumu/items/978df93755c4720d5cdc)
- [Accessibility tree (アクセシビリティツリー) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN](https://developer.mozilla.org/ja/docs/Glossary/Accessibility_tree)
- [フォームの input 要素には表示可能なラベルが必要です ](https://dequeuniversity.com/rules/axe/4.3/label-title-only?lang=ja)
- [alt属性の良い事例(つけ方・書き方)｜情報バリアフリーポータルサイト](http://jis8341.net/jirei_sample/jirei_chapter_01.html)
- [フォーカスの分類 | アクセシビリティBlog | ミツエーリンクス](https://www.mitsue.co.jp/knowledge/blog/a11y/201912/23_0000.html)
  