LoginSignup
7
2

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

Last updated at Posted at 2023-11-30

はじめに

みなさんアクセシビリティを意識して開発できていますか?

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

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

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

アクセシビリティを意識したスイッチボタンの仕様

⚪︎ スイッチボタンとは?

スイッチは、ユーザーが二つの値(オン/オフ)を選択できるコンポーネントです。
スイッチはチェックボックスやトグルボタンに似て、スイッチも二進入力として機能します。
スイッチは オン/オフ のみに使用できるのに対し、チェックボックスやトグルボタンは三つ目の中間状態をサポートするオプションを選択できます。
チェックボックスが選択できる状態は、チェックされている/チェックされていない/部分的にチェックされている を選択できます。
トグルボタンが選択できる状態は、押されている/押されていない/部分的に押された を選択できます。

スイッチ、チェックボックス、トグルボタンはすべて二進入力を提供しているため互換性があります。
そのため、視覚的なデザインとセマンティック的に適したコンポーネントを選択する必要があります。

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

  • Spaceキー
    • スイッチにフォーカスがある場合、スイッチの状態を変更する
  • Enterキー(任意)
    • スイッチにフォーカスがある場合、スイッチの状態を変更する

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

  • スイッチには role="switch" を設定する
  • スイッチには、以下のいずれかでアクセス可能なラベルを提供する
    • role="switch" を持つ要素内に含まれる 可視テキストコンテンツ
    • role="switch" を持つ要素に設定された aria-labelledby の値で参照される可視ラベル
    • role="switch" を持つ要素に設定された aria-label
  • オンの状態のとき、スイッチ要素には aria-checked="true" を設定する
  • オフの状態のとき、スイッチ要素には aria-checked="false" を設定する
  • スイッチ要素が input[type="checkbox"] の場合、aria-checked の代わりに checked を使用する
  • スイッチのセットが可視ラベルを持つセマンティックなグループの場合、次のいずれかになる
    • スイッチは、グループラベルを含む要素のIDが設定された aria-labelledby を持つ role="group" を持つ要素に含まれる
    • スイッチのセットは <fieldset> 内に含まれ、ラベルは <legend> に含まれる
  • スイッチまたはスイッチグループに関する追加のテキスト情報が含まれている場合、スイッチまたはスイッチグループには、説明を含む要素のIDを設定した aria-describedby を設定する

アクセシビリティを意識したスイッチボタンの完成形

See the Pen Spinbutton Pattern Accessibility by でぐぅー | Qiita (@sp_degu) on CodePen.

アクセシビリティを意識したスイッチボタンの作り方

1. HTMLを実装する

sample.html
<fieldset class="container">
  <button
    role="switch"
    aria-label="スイッチ"
    aria-checked="false"
  ></button>
  <button
    role="switch"
    aria-label="スイッチ"
    aria-checked="true"
  ></button>
</fieldset>

2. 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;
}

.container {
  align-items: center;
  backdrop-filter: blur(15px);
  background-blend-mode: luminosity;
  background-color: rgb(128 128 128 / .3);
  border: none;
  border-radius: 18px;
  display: flex;
  gap: 40px;
  justify-content: center;
  padding: 16px;
  position: relative;
  max-width: 300px;
  min-width: 150px;
  width: 100%;
  &::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: 18px;
    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[role="switch"] {
  border: none;
  border-radius: 32px;
  box-shadow: 1px 1.5px 4px 0px rgba(0, 0, 0, 0.10) inset, 1px 1.5px 4px 0px rgba(0, 0, 0, 0.08) inset, 0px -0.5px 1px 0px rgba(255, 255, 255, 0.25) inset, 0px -0.5px 1px 0px rgba(255, 255, 255, 0.30) inset;
  cursor: pointer;
  height: 36px;
  position: relative;
  width: 62px;
  &::before {
    background-color: #ffffff;
    border-radius: 50%;
    content: "";
    display: block;
    height: 28px;
    position: absolute;
    top: 4px;
    width: 28px;
  }
  &[aria-checked="false"] {
    background: linear-gradient(0deg, rgba(0, 0, 0, .1) 0%, rgba(0, 0, 0, 0.10) 100%), rgba(0, 0, 0, .5);
    background-blend-mode: luminosity, color-burn;
    &::before {
      left: 4px;
    }
  }
  &[aria-checked="true"] {
    background: linear-gradient(0deg, rgba(208, 208, 208, 0.50) 0%, rgba(208, 208, 208, 0.50) 100%), #32D74B;
    background-blend-mode: color-burn, normal;
    &::before {
      right: 4px;
    }
  }
}

3. JavaScriptを実装する

sample.js
document.addEventListener('DOMContentLoaded', () => {
  const switchButtons = document.querySelectorAll('button[role="switch"]');
  switchButtons.forEach((switchButton) => {
    switchButton.addEventListener('click', (e) => {
      updateSwitchButton(e.target);
    });
  });
  
  function updateSwitchButton(switchbutton) {
    const currentState = switchbutton.getAttribute("aria-checked") === 'true';
    const newState = String(!currentState);
    switchbutton.setAttribute('aria-checked', newState);
  }
 });

まとめ

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

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

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


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

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

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2