【アクセシビリティ】アクセシビリティを意識したボタンの作り方

## はじめに
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390720/02e5dad4-aa0e-8fd1-b193-0f3ab21fbbea.png)
みなさんアクセシビリティを意識して開発できていますか？

必要なところに`role属性`を記述したり、`tabキー`でフォーカスができるようにしたりなど、意識しないといけないことも多いです。
そのため、アクセシビリティを完璧にやろうとするのは一苦労です。

ただ、コンポーネントごとに区切って、アクセシビリティを理解しておけば、実装するタイミングに思い出しやすく、アクセシビリティも意識しやすいと思います。

そのため、この記事では「**ボタン**」に焦点を当てて、アクセシビリティを意識したボタンの実装方法とボタンで意識した方がいいアクセシビリティを解説しようと思います。

## アクセシビリティを意識したボタンの仕様
### ⚪︎ ボタンとは？
ボタンは、フォームの送信、ダイアログのオープン、アクションのキャンセル、削除操作など、ユーザーがアクションやイベントをトリガーできるようにする要素です。
ただ、リンクの機能(ページ遷移、ページ内遷移等)の役割とは違います。

ボタンには、通常のボタンのほかに以下の2種類のボタンがあります。

+ **トグルボタン**
  + オン（押された状態）/オン（押されてない状態）の2つの状態を持つボタン
  + `aria-pressed`を指定することで、支援技術にトグルボタンであることが伝わる
    + ただ、オン/オフの状態がわかるラベルの場合は必要ない
+ **メニューボタン**
  + メニューを開くためのボタン
  + `aria-haspopup`に`menu`or`true`を指定することで、支援技術にメニューボタンであることが伝わる

### ⚪︎ キーボードインタラクション

+ **`Spaceキー`**
  + アクションやイベントをアクティブにする
  + 現在のコンテキストが変わらない場合
    + → フォーカスはボタンに残る
  + 現在のコンテキストが変わった場合
    + → 最初のフォーカス可能な要素に移動する
  + ボタンがダイアログを開く場合
    + → ダイアログ内に移動する
+ **`Enterキー`**
  + アクションやイベントをアクティブにする
  + 現在のコンテキストが変わらない場合
    + → フォーカスはボタンに残る
  + 現在のコンテキストが変わった場合
    + → 最初のフォーカス可能な要素に移動する
  + ボタンがダイアログを開く場合
    + → ダイアログ内に移動する

### ⚪︎ WAI-ARIA の役割、状態、プロパティ

+ `<button>`には`role="button"`の役割がある
+ ボタンには、アクセス可能なラベルが必要。
  + デフォルトは、ボタン内のテキスト
  + `aria-label` や `aria-labelledby`で提供することはできる
+ ボタンに機能の説明がある場合、`<button>`に`aria-describedby`で説明している要素のIDを指定する
+ アクションがが利用できない時は、`aria-disabled="true"`を指定する
+ ボタンがトグルボタンの場合`aria-pressed`を指定します。
  + トグルがオンの場合 → `aria-pressed="true"`
  + トグルがオフの場合 → `aria-pressed="false"`

## アクセシビリティを意識したボタンの完成形
<p class="codepen" data-height="500" data-default-tab="result" data-slug-hash="ExGzbya" data-editable="true" data-user="sp_degu" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/sp_degu/pen/ExGzbya">
  Button Accessibility</a> by でぐぅー | Qiita (<a href="https://codepen.io/sp_degu">@sp_degu</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## アクセシビリティを意識したボタンの作り方
### 1. HTMLを実装する
```sample.html
<div class="buttons-container">
  <button id="default-button">
    ボタン
  </button>
  <button id="toggle-button" aria-pressed="false">
    トグルボタン：OFF
  </button>
</div>
```

### ２. CSSを実装する
```sample.css
/*　以下スタイル調整　*/
body {
  background-color: #212529;
  color: #fff;
  display: grid;
  height: calc(100vh - 40px);
  margin: 0;
  padding: 20px 0;
  place-items: center;
  width: 100vw;
}

.buttons-container {
  background-color: rgb(128 128 128 / .3);
  border-radius: 24px;
  padding: 16px;
  position: relative;
  min-width: 300px;
  background-blend-mode: luminosity;
  backdrop-filter: blur(15px);
}

.buttons-container::before {
  background: linear-gradient(135deg, rgb(255 255 255 / .4) 0, rgb(255 255 255 / 0) 40%, rgb(255 255 255 / 0) 60%, rgb(255 255 255 / .1) 100%);
  border: 1.4px solid transparent;
  border-radius: 24px;
  content: "";
  inset: 0;
  position: absolute;
  -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0) border-box;
  -webkit-mask-composite: destination-out;
  mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0) border-box;
  mask-composite: exclude;
  z-index: -1;
}

button {
  align-items: center;
  background: none;
  border: none;
  border-radius: 100px;
  color: #ffffff;
  cursor: pointer;
  display: flex;
  font-size: 16px;
  font-weight: bold;
  justify-content: center;
  padding: 8px 16px;
  width: 100%;
}

button + button {
  margin-top: 8px;
}

button:hover {
  background: radial-gradient(34.12% 136.61% at 50% 100%, rgba(94, 94, 94, 0.14) 0%, rgba(94, 94, 94, 0.00) 73.85%), radial-gradient(50% 164.29% at 50% 100%, rgba(255, 255, 255, 0.07) 0%, rgba(255, 255, 255, 0.00) 60.33%), linear-gradient(0deg, rgba(94, 94, 94, 0.18) 0%, rgba(94, 94, 94, 0.18) 100%), rgba(255, 255, 255, 0.06);
  background-blend-mode: color-dodge, normal, color-dodge, lighten;
}

button[aria-pressed="true"] {
  background: linear-gradient(0deg, rgba(94, 94, 94, 0.18) 0%, rgba(94, 94, 94, 0.18) 100%), rgba(255, 255, 255, 0.07);
  background-blend-mode: color-dodge, normal;
}

button[aria-pressed="true"]:hover {
  background: radial-gradient(101.08% 100% at 50% 100%, rgba(94, 94, 94, 0.32) 0%, rgba(94, 94, 94, 0.00) 73.85%), radial-gradient(100.02% 100% at 50% 100%, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.00) 68.75%), linear-gradient(0deg, rgba(94, 94, 94, 0.18) 0%, rgba(94, 94, 94, 0.18) 100%), rgba(255, 255, 255, 0.06);
  background-blend-mode: color-dodge, normal, color-dodge, lighten;
}
```

### 3. JavaScriptを実装する
```sample.js
window.addEventListener('load', function () {
  const toggleButton = document.getElementById('toggle-button');
  
  toggleButton.addEventListener('click', switchToggleButton);
})

function switchToggleButton() {
  const toggleButton = document.getElementById('toggle-button');
  const status = toggleButton.getAttribute("aria-pressed");
  
  if (status === "false") {
    toggleButton.setAttribute("aria-pressed", "true");
    toggleButton.innerText = "トグルボタン：ON";
  } else {
    toggleButton.setAttribute("aria-pressed", "false");
    toggleButton.innerText = "トグルボタン：OFF";
  }
}
```

## まとめ
この記事では、「**ボタン**」に焦点を当てて、アクセシビリティを意識したボタンの実装方法とボタンで意識した方がいいアクセシビリティを解説しました。

ぜひこの記事をストックして、ボタンを実装する時にアクセシビリティについて思い出してもらえると嬉しいです。

Advent Calendar 2023では、他のコンポーネントにも焦点を当てて、アクセシビリティについても解説しているので、ぜひ購読していてください。

https://qiita.com/advent-calendar/2023/degudegu2510

---

最後まで読んでくださってありがとうございます！

普段はデザインやフロントエンドを中心にQiitaに記事を投稿しているので、ぜひQiitaのフォローとX(Twitter)のフォローをお願いします。

https://twitter.com/degudegu2510