4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

コンポーネントごとに考えるアクセシビリティAdvent Calendar 2023

Day 10

【アクセシビリティ】アクセシビリティを意識したディスクロージャの作り方

Last updated at Posted at 2023-12-09

はじめに

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

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

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

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

アクセシビリティを意識したディスクロージャの仕様

⚪︎ ディスクロージャとは?

ディスクロージャは、コンテンツを折りたたんだり(非表示)、展開させる(表示)ことができる要素です。
ディスクロージには、表示/非表示を制御するボンタンと、表示/非表示する要素の2要素があります。

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

  • Enterキー
    • コンテンツの表示/非表示を制御するボタンにフォーカスがある場合、コンテンツの表示/非表示を制御します
  • Spaceキー
    • コンテンツの表示/非表示を制御するボタンにフォーカスがある場合、コンテンツの表示/非表示を制御します

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

  • コンテンツの表示/非表示を制御するボタンには、role="button" を設定します
  • コンテンツが表示されている場合、ボタン要素に aria-expanded="true" を設定します
  • コンテンツが非表示の場合、ボタン要素に aria-expanded="false" を設定します
  • (任意)ボタン要素に、コンテンツ要素のIDを aria-controls に設定します

アクセシビリティを意識したディスクロージャの完成形

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

アクセシビリティを意識したディスクロージャの作り方

1. HTMLを実装する

sample.html
<div class="disclosure">
  <button class="disclosure-button" type="button" aria-expanded="false" aria-controls="faq1_desc">
    <span class="material-symbols-outlined">chevron_right</span>
    Qiitaとは?
  </button>
  <div id="faq1_desc" class="desc">
    Qiita (キータ) は、エンジニアに関する知識を記録・共有するためのサービスです。
  </div>
  <button class="disclosure-button" type="button" aria-expanded="false" aria-controls="faq2_desc">
    <span class="material-symbols-outlined">chevron_right</span>
    バッジ機能とは?
  </button>
  <div id="faq2_desc" class="desc">
    バッジ機能は、イベントへの参加やユーザー表彰プログラムの達成など、 特定の条件を達成した方にバッジをお送りする機能です。
  </div>
</div>

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

.disclosure {
  background-color: rgb(128 128 128 / .3);
  border-radius: 24px;
  padding: 16px;
  position: relative;
  width: 400px;
  background-blend-mode: luminosity;
  backdrop-filter: blur(50px);
  &::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;
  color: #ffffff;
  display: flex;
}

.disclosure-button {
  cursor: pointer;
  font-size: 16px;
  padding: 8px;
  width: 100%;
  &[aria-expanded="true"] > .material-symbols-outlined {
    transform: rotate(90deg);
  }
}

.material-symbols-outlined {
  padding: 4px;
}

.desc {
  margin: 0 8px;
  padding: 8px 16px;
  border-radius: 8px;
  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;
}

3. JavaScriptを実装する

sample.js
class DisclosureButton {
  constructor(buttonNode) {
    this.buttonNode = buttonNode;
    this.controlledNode = false;

    var id = this.buttonNode.getAttribute('aria-controls');

    if (id) {
      this.controlledNode = document.getElementById(id);
    }

    this.buttonNode.setAttribute('aria-expanded', 'false');
    this.hideContent();

    this.buttonNode.addEventListener('click', this.onClick.bind(this));
    this.buttonNode.addEventListener('focus', this.onFocus.bind(this));
    this.buttonNode.addEventListener('blur', this.onBlur.bind(this));
  }

  showContent() {
    if (this.controlledNode) {
      this.controlledNode.style.display = 'block';
    }
  }

  hideContent() {
    if (this.controlledNode) {
      this.controlledNode.style.display = 'none';
    }
  }

  toggleExpand() {
    if (this.buttonNode.getAttribute('aria-expanded') === 'true') {
      this.buttonNode.setAttribute('aria-expanded', 'false');
      this.hideContent();
    } else {
      this.buttonNode.setAttribute('aria-expanded', 'true');
      this.showContent();
    }
  }

  onClick() {
    this.toggleExpand();
  }

  onFocus() {
    this.buttonNode.classList.add('focus');
  }

  onBlur() {
    this.buttonNode.classList.remove('focus');
  }
}

window.addEventListener(
  'load',
  function () {
    var buttons = document.querySelectorAll(
      'button[aria-expanded][aria-controls]'
    );

    for (var i = 0; i < buttons.length; i++) {
      new DisclosureButton(buttons[i]);
    }
  },
  false
);

まとめ

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

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

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


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

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

4
1
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
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?