JavaScriptでWebアクセシビリティに配慮したボタンを作ろう

こんにちは！

# 概要

通常の`<button>`要素ではなく、カスタムなボタンを実装する時に、W3のWebアクセシビリティの基準を満たすHTML要素の実装方法を説明します

# なぜWebアクセシビリティ

我々Web開発界隈で最も軽視されがち、誤解されがちなWebアクセシビリティ。多くのジュニアおよびミドル級のWeb開発者にはまずWebアクセシビリティの存在自体に気づいていないのかもしれません。またより愚かなパターンですが、知っているけれど陰謀論のような思想家でWebアクセシビリティに反感を持っている者すらいます。

なので、まずは「なぜWebアクセシビリティが大事なのか」というところから話を始めたいのですが、すでに目から鱗が落ちている方はこの独白スピーチをスキップしてください:joy:

Webアクセシビリティは、[W3](https://www.w3.org/WAI/fundamentals/accessibility-intro/)の説明を拝借しますと、
> 障害を持っているユーザーでも難なく使えるWebサイトをように設計および開発する技術

のことです。

しかし、これもよく誤解されているのですが、**ただただ障害者のためばかりではないのです**。W3も力強く論じてくれますが、健常者のためにもWebアクセシビリティは非常に重要です。なぜなら、**障がい者でも使えるWebサイトは、健常者にも使えるわけだから**、です。

近年のWeb開発は、以前筆者が触れたように「失われた10年間」と批判されています。ReactのようなJavaScript中心のフレームワークは、ますますWebを**in**accessibleなものにしたのです。健常者にとっても、障害持ちの方にとっても。これは我々開発者でも厳しく己を評価すれば、わかることです。___最近のWebアプリは、使えないんです。___

そこで、やはりWebアクセシビリティを大切にしよう、という話になってきます。実際、Webアクセシビリティに配慮すればSEO効果、ビジネスの効果も明らかで、ユーザーを喜ばせるだけのためではないのも言うまでもないことです。

ただ、Webアクセシビリティは _くそ難しい_ 時があるのです。筆者でさえ今もよく間違えています。ましてやReactのようなJSフレームワークの中で実現するなんて想像を絶するほどの難儀でしょう。

___それでもやらねばならないのです___。難しいから逃げるのでは一流にはなれないとか、名言めいた言葉を吐くところですが、やめておきます。

ちなみに、アメリカでは、障害持ちの方に使えないWebサイトを作ることが差別だという観点からADA（Americans with Disabilities Act）が改訂され、Webアクセシビリティが施されていないと法律違反として罰せられるようになりました。EUにもWAD（Web Accessibility Directive）の法律があり、国際的なアプリを公開する場合、Webアクセシビリティに対応しないといけません。日本も遅れているのですが、そのうちWebアクセシビリティ関連の法律が制定されることでしょう。

# W3基準を満たしたカスタムボタン要素を作る

まずはHTMLからいきましょう。今回の要素は、ユーザーに表示する文字はなく、SVGなどのようなアイコンにする前提で作ります。

表示する文字列がないので、[`aria-label`](https://developer.mozilla.org/ja/docs/Web/Accessibility/ARIA/Attributes/aria-label)が必要になります。
また、ユーザーにこの要素の役割を支えるために、[`role="button"`](https://developer.mozilla.org/ja/docs/Web/Accessibility/ARIA/Roles/button_role)の指定も必要です。さらに、キーボードのみ使っているユーザーがTabキーでフォーカスできるように、[`tabindex="0"`](https://developer.mozilla.org/ja/docs/Web/HTML/Global_attributes/tabindex)の属性も必要です。

```html
<div
    class="delete-button"
    aria-label="押して削除する"
    tabindex="0"
    role="button"
>
    <!-- SVG? -->
</div>
```

これで目の不自由なユーザーにも朗読ソフトを通してこの要素の役割が伝わるようになりますし、キーボードユーザーもTabキーで選択できるようにもなりました。

ただ、これだけだと無論、まだ機能しません。

ここでJavaScriptを使って適切な動作を実装します。

今回の場合は、何らかのEventListenerをつけますが、`click`のイベントのみ受信するようにすれば、**キーボードユーザーには使えなくなってしまいます**。なので、`keydown`イベントも受信しますが、キーを押せばいいわけではなく、W3の基準を遵守して、**空白キー(Spacebar)とEnterキー**を押したかどうかの判定も必要です。

https://www.w3.org/WAI/ARIA/apg/patterns/button/examples/button/

W3のページに細かく仕様が記載されています。

JavaScriptで簡単に実装してみましょう。

```javascript
const deleteButton = document.querySelector('.delete-button');

function myCallback() {
    // open a modal or something?
}

deleteButton.addEventListener('click', myCallback);
deleteButton.addEventListener('keydown', e => {
    if (e.key === 'Enter' || e.key === ' ') {
        myCallback();
        e.preventDefault();
    }
})
```

これでWebアクセシビリティに配慮したボタンのHTMLとJavaScriptが書けます！

CSSも目の不自由な方に配慮するためのガイドラインがありますが、本記事では触れません。

## ボーナス：RxJSを使ってイベントの整理をする

他のロジックとイベント受信が絡んでくるとすぐに手に負えなくなりますし、上記のような書き方だと不便に感じませんか？

筆者は唯一愛用しているJavaScriptライブラリーのRxJSを使ってWebアクセシビリティの実装を普段しています。

```typescript
import {Observable, filter, fromEvent, tap, merge} from 'rxjs';

/**
 * Fire event when enter key or spacebar is pressed
 */
export function fromEnterKeydownEvent(
  target: HTMLElement
): Observable<KeyboardEvent> {
  return fromEvent<KeyboardEvent>(target, 'keydown').pipe(
    filter(e => e.key === 'Enter' || e.key === ' '),
    tap(e => e.preventDefault())
  );
}

const editIcon = this.querySelector<HTMLElement>('.edit-icon')!;
const dialog = this.querySelector('dialog')!;

merge(
   fromEvent(editIcon, 'click'),
   fromEnterKeydownEvent(editIcon)
).pipe(
    // switchMapか何かで読み込んでから開く？
).subscribe(() => dialog.showModal());

```

RxJSの愛好家の仲間がいれば、こんな感じに活用してくれたら嬉しいです:grin:

# まとめ

以上、小さいことですが、これでWebアクセシビリティに配慮したカスタムなボタンが作れます。いかがでしょうか？

Webアクセシビリティ（a11y）は、最初は大変そうに思えて敬遠している方も多いかもしれませんが、普段から少しばかりの配慮を払っていれば、スピーディーな開発をキープしつつ、健常者にも障害者にも喜ばれるようなWebサイトがさくさく作れるようになってきます。

a11yも一気にやるのではなく、できる範囲、気づいたところからやっていけば効果があるので、まずは小さいボタン一つでも、やっていきませんか？

したっけ！