【アクセシビリティ】アクセシビリティを意識したスライダーの作り方

## はじめに
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390720/bc02f5a5-98f8-6617-e8d0-21cc82e53eb2.png)

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

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

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

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

## アクセシビリティを意識したスライダーの仕様
### ⚪︎ スライダーとは？
スライダーは、ユーザーが与えられた範囲内から値を選択する入力方法の1つです。
スライダーは通常、バー、レール、またはトラックに沿って動かすことができるスライダーサムを持ち、それによってスライダーの値を変更します。

:::note warn
タッチベースの支援技術を使用するユーザーは、支援技術が提供するジェスチャーがまだ必要な出力を生成しない可能性があるため、スライダーの入力方法に困難するかもしれません。
:::

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

+ **`→キー`**
  + スライダーの値を1ステップ増加させる
+ **`↑キー`**
  + スライダーの値を1ステップ増加させる
+ **`←キー`**
  + スライダーの値を1ステップ減少させる
+ **`↓キー`**
  + スライダーの値を1ステップ減少させる
+ **`Homeキー`**
  + スライダーをその範囲の最初の許容値に設定する
+ **`Endキー`**
  + スライダーをその範囲の最後の許容値に設定する
+ **`Page Upキー`**（任意）
  + 上矢印によるステップ変更よりも大きい量でスライダーの値を増加させる
+ **`Page Downキー`**（任意）
  + 上矢印によるステップ変更よりも大きい量でスライダーの値を減少させる

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

+ フォーカス可能なスライダーコントロールとして機能する要素には `role="slider"` を設定する
+ スライダー要素には、スライダーの現在値を表す値を `aria-valuenow` で設定する
+ スライダー要素には、スライダーの最小許容値を表す値を `aria-valuemin` で設定する
+ スライダー要素には、スライダーの最大許容値を表す値を `aria-valuemax` で設定する
+ aria-valuenowの値がユーザーフレンドリーでない場合、`aria-valuetext` で理解しやすい文字列で設定する
  + `aria-valuetext="月曜日"`
+ スライダーに可視ラベルがある場合、スライダー要素上のaria-labelledbyで参照される
  + そうでない場合、スライダー要素にはaria-labelによってラベルを提供する
+ スライダーが垂直方向に配置されている場合、`aria-orientation="vertical"` を設定する
  + スライダーは、`aria-orientation="horizontal"` がデフォルト値

## アクセシビリティを意識したスライダーの完成形
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="QWYxqJX" data-editable="true" data-user="sp_degu" style="height: 300px; 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/QWYxqJX">
  Meter Accessibillity</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="container">
  <input
    id="meter"
    type="range"
    aria-label="スライダー"
    min="0"
    aria-valuemin="0"
    max="10"
    aria-valuemax="10"
    value="5"
    aria-valuenow="5"
  >
  </input>
</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;
}

.container {
  backdrop-filter: blur(50px);
  background-color: rgb(128 128 128 / .3);
  background-blend-mode: luminosity;
  border-radius: 32px;
  max-width: 400px;
  padding: 20px;
  position: relative;
  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: 32px;
    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;
  }
}

input[type="range"] {
  appearance: none;
  background: none;
  border-radius: 8px;
  display: block;
  max-width: 400px;
  overflow: hidden;
  width: 100%;
  &::-webkit-slider-runnable-track {
    background-blend-mode: color-dodge, lighten;
    height: 16px;
  }
  
  &::-webkit-slider-thumb {
    appearance: none;
    background: radial-gradient(#ffffff 6px, #ababab 6px);
    border-radius: 0 8px 8px 0;
    height: 16px;
    width: 16px;
  }
}
```

### 3. JavaScriptを実装する
```sample.js
document.addEventListener('DOMContentLoaded', () => {
  const sliders = document.querySelectorAll('input[type="range"]');
  sliders.forEach((slider) => {
    slider.addEventListener('input', (e) => {
      updateSlider(e.target);
    });
    updateSlider(slider);
  });
 
  function updateSlider(slider) {
    const bc = '#242424';
    const ac = '#ababab';
    if (slider.value >= slider.max / 2) {
      const progress = (slider.value / slider.max) * 100 - 1;
      slider.style.background = `linear-gradient(to right, ${ac} ${progress}%, ${bc} ${progress}%)`;
    } else {
      const progress = (slider.value / slider.max) * 100 + 1;
      slider.style.background = `linear-gradient(to right, ${ac} ${progress}%, ${bc} ${progress}%)`;
    }
    slider.ariaValueNow = slider.value;
  }
});
```

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

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

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

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

---

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

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

https://twitter.com/degudegu2510