アクセシビリティを考慮したWebデザイン: detailsとsummaryタグの使い方

この記事では、details・summaryタグを使用したアコーディオン式UIにアニメーション効果を追加し、アクセシビリティを向上させる方法を紹介します。GSAP(GreenSock Animation Platform)ライブラリを活用したJavaScript、ほかCSSによる装飾等、コードの実装について詳細に解説しています。


以前、アコーディオン式UI(ユーザーインターフェイス)をdetailssummaryタグで作成する方法をご紹介しました。

rubirubi.hateblo.jp

今回は、改めてdetails・summaryタグの機能を見直し、より良いアクセシビリティを提供するアニメーション効果を追加します。

デモページ

デモページを見る

details、summaryタグの利点

details・summaryタグは、シンプルな記述で以下の機能を実装します。

  • キーボードイベント登録不要: JavaScriptのキーボードイベントを登録することなく、detailsおよびsummaryタグを使用してタブフォーカスとエンターキー・スペースキーによる開閉操作が実現可能。

  • アクセシビリティ: スクリーンリーダーが開閉状態について的確に読み上げ、閉じた状態、開いた状態それぞれの情報が提供される。

  • 単語検索と連携: サイト内で単語を検索した時、アコーディオンが閉じた状態でも中に検索した単語が含まれる場合はアコーディオンが開き、ユーザーは中身の単語に直接移動できる。

  • シンプルな操作性: detailsおよびsummaryタグを利用することで、シンプルで効果的なUIを実現し、ユーザーが直感的にコンテンツを開閉できる。

開閉過程にアニメーションを追加

上記のように多くのメリットがあるdetails・summaryタグですが、そのままだと開閉が瞬間的に変わるため、一部の利用者にとっては少し理解しにくい要素となり得ます。特に視覚的な制約を抱えるユーザーなど、急激な変化は適切な情報取得を妨げる可能性があります。

ここからは、このデメリットを踏まえ、開閉過程にアニメーションを取り入れ、ユーザーエクスペリエンスの向上を図ります。

コードはHTML -> JavaScript -> CSSの順に紹介していきます。

details、summaryタグの基本構造

<details><summary>の基本的な構造は、

  1. <details><summary>(概要)と詳細情報(details)を囲む配置とし、
  2. summaryをクリックすることで詳細の表示/非表示が切り替わります。

以下のHTMLコードではsummaryをカスタマイズし、メッセージの切り替えやアイコン、アニメーションを付与するためのクラス付けをしています。

HTML

<span>にcloseとopenというクラスを付与し、スクリプトで表示する要素を切り替え、開閉状態それぞれで異なるメッセージを表示させます。

コードと解説

コードが閉じます コードが開きます

<details class="header-sample js-details" close="">
  <!-- <summary> 要素は折り畳まれたコンテンツのヘッダーを定義します -->
  <summary class="floaton-ukimasu stretche-line js-summary">
    <!-- 閉じられた状態の <summary> -->
    <span class="summary_inner close" style="color: #d32f2f">
      <a><strong><em>コードが閉じます</em></strong></a>
      <span class="ds-icon"></span>
    </span>
    <!-- 開いた状態の <summary> -->
    <span class="summary_inner open" style="color: #d32f2f">
      <a><strong><em>コードが開きます</em></strong></a>
      <span class="ds-icon"></span>
    </span>
  </summary>

  <!-- <div class="content"> は折り畳まれたコンテンツの実際の内容を定義します -->
  <div class="content js-content" style="opacity: 1; height: auto;">
    <div class="content_inner">
      <!-- 折りたたまれたコンテンツの個々の項目 -->
      <li>折りたたむ内容</li>
      <li>折りたたむ内容</li>
      <li>折りたたむ内容</li>
    </div>
  </div>
</details>

ライブラリ(GSAP)

下記のサイトを参考とさせていただき、アニメーション効果にはGSAP(ジーサップ)ライブラリを利用しています。

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>

GSAPとは?

GSAP(GreenSock Animation Platform)は、GreenSock社が開発したJavaScriptのアニメーションライブラリです。GSAPは軽量でありながら多機能で、シンプルな記法でアニメーションを実装できるため、多くの開発者に利用されています。

GSAPの特徴について、以下のようなポイントが挙げられます:

  1. 軽量で多機能かつ直感的:

    • GSAPは軽量ながらも高度なアニメーション機能を提供しています。また、その記法は直感的で理解しやすいものとなっています。
  2. スムーズで高い自由度:

    • GSAPのアニメーションは非常にスムーズで、複雑なアニメーションやタイミング制御が得意です。開発者は要素の移動、回転、拡大縮小、透明度の変更など、細かいアニメーション効果を容易に制御できます。
  3. ユーザーエクスペリエンス向上の機能提供:

    • GSAPは、ウェブサイトやアプリのユーザーエクスペリエンスを向上させるために必要な機能を豊富に提供しています。アニメーションに関する機能だけでなく、タイムライン制御やEasingなど、高度な機能も備えています。
  4. UIアニメーション制作に特化:

    • GSAPはUIアニメーション制作に特化しており、ウェブサイトやアプリのモーション制作に非常に役立ちます。豊富な機能と柔軟性により、幅広いアニメーションの制作に対応しています。

総じて、GSAPはウェブ開発における有効な選択肢であり、その機能を活かして滑らかで洗練されたアニメーションを容易に組み込むことができます。

jQueryとの違い

GSAPとjQueryは、いずれもウェブ開発においてアニメーションやDOM操作を行うためのツールですが、いくつか重要な違いがあります。

  1. アプローチの違い:

    • jQuery: jQueryは広く用いられる汎用的なJavaScriptライブラリで、DOM操作、イベント処理、アニメーションなどを簡略化することが目的です。アニメーションはanimate()メソッドを使用して行います。
    • GSAP: GSAPは専門的にアニメーションに焦点を当てたライブラリで、高度なアニメーション機能やイージング、タイムラインなどが豊富に提供されています。
  2. 性能:

    • jQuery: jQueryは全般的なDOM操作としての性能が考慮されていますが、複雑なアニメーションや高度な制御においては、性能が向上することがありません。
    • GSAP: GSAPは高いパフォーマンスを提供し、複雑なアニメーションにも対応しています。特にモバイルデバイスや複雑なアプリケーションでの利用に優れています。
  3. アニメーションの柔軟性:

    • jQuery: jQueryは基本的なアニメーション機能を提供していますが、高度なアニメーションやタイミング制御においては、GSAPに比べて柔軟性が低いとされています。
    • GSAP: GSAPはタイムラインやEasingといった機能が豊富であり、アニメーションの制御が非常に柔軟です。
  4. サイズ:

    • jQuery: jQueryは幅広い機能を提供しているため、ファイルサイズが比較的大きいです。
    • GSAP: GSAPはアニメーションに特化しているため、ファイルサイズが小さく、必要なモジュールのみを使用することができます。

どちらを選ぶかはプロジェクトのニーズに依存します。小規模なプロジェクトや既存のjQueryのコードとの統合が容易な場合はjQueryが適しているかもしれませんが、複雑なアニメーションや高度な制御が必要な場合はGSAPがより適していることがあります。

JavaScript

GSAPライブラリを利用した以下のJavaScriptコードにより、アコーディオンの開閉時にアニメーション効果を追加します。

主に

  • 透明度の変化と
  • details下部にある要素のスクロールアクション

に焦点を当てています。

コードと解説

コードが閉じます コードが開きます

// ドキュメントの読み込みが完了してから setUpAccordion 関数を呼び出す
document.addEventListener("DOMContentLoaded", () => {
  setUpAccordion();
});

// js-details クラスを持つすべての要素に対して、クリック時の挙動を定義
const setUpAccordion = () => {
  let elements = document.querySelectorAll(".js-details");
  let openedClass = "is-opened";

  // 各アコーディオン要素に対して処理を行う
  elements.forEach(element => {
    let summary = element.querySelector(".js-summary");
    let content = element.querySelector(".js-content");

    // サマリーがクリックされたときの処理
    summary.addEventListener("click", event => {
      event.preventDefault();

      // アコーディオンが開いているかどうかを判定して、それに応じて処理を行う
      if (element.classList.contains(openedClass)) {
        // 開いている場合は閉じるアニメーションを実行
        element.classList.toggle(openedClass);
        closingAnim(content, element).restart();
      } else {
        // 閉じている場合は開くアニメーションを実行
        element.classList.toggle(openedClass);
        element.setAttribute("open", "true");
        openingAnim(content).restart();
      }
    });
  });
};

// GSAPの to メソッドを使用してアコーディオンを閉じるアニメーションを定義
const closingAnim = (element, accordionElement) => gsap.to(element, {
  height: 0,  // 要素の高さ
  opacity: 0,  // 不透明度
  duration: 1.4,
  ease: "power3.out",
  overwrite: true,
  onComplete() {
    accordionElement.removeAttribute("open");  // アニメーション完了時、open 属性を削除
  }
});

// アコーディオンを開くアニメーションを定義
const openingAnim = element => gsap.fromTo(element, {
  height: 0,
  opacity: 0
}, {
  height: "auto",
  opacity: 1,
  duration: 1.4,
  ease: "power3.out",
  overwrite: true
});

解説の補足

スクリプトによりdetailsタグは以下の状態に変化します。

  • 初期状態
    <details class="header-sample js-details" close="">
  1. 閉じている時にクリックした場合
    <details class="header-sample js-details is-opened" close="" open="true">
  2. 開いている時にクリックした場合(初期状態に戻る)
    <details class="header-sample js-details" close="">

ボタンの装飾

  • マウスホバーで浮き上がり&アンダーラインのアニメーション効果
  • ボタンクリックでアイコンを置換&アニメーション

CSS

以下のCSSコードにより、detailsタグの見た目をボタン仕様に装飾します。さらにマウスホバー、クリック時にはアイコンやアンダーラインなどによるアニメーション効果を実装します。

コードと解説

コードが閉じます コードが開きます

/* コンテンツのオーバーフロー設定 */
.content {
  overflow: hidden;
}
/* コンテンツ内部のスタイル */
.content_inner {
  padding: 0;
  flex-direction: column;
  gap: 16px;
}

/* summary要素の基本スタイル */
summary {
  display: block;
}
/* Webkitデフォルトマーカー非表示 */
summary::-webkit-details-marker {
  display: none;
}
/* summary内のテキストスタイル */
.summary_inner {
  display: flex;
  padding: 16px 24px;
  color: #025;
}
/* summaryのリンクスタイル */
.floaton-ukimasu>span>a {
  position: relative;
  margin-left: 25px;
  color: #4d5156;
}
/* summaryが開いている場合のアイコン設定 */
details[open] summary.floaton-ukimasu::before {
  content: "\f07c";
}

/* ボタン要素の基本スタイル */
.floaton-ukimasu {
  transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
  transform: translateY(0);
  font-family: 'Helvetica', 'Arial', sans-serif;
  display: inline-block;
  padding: 0em 1em 0 35px;
  margin-top: 15px;
  margin-bottom: 15px;
  border-radius: 10px;
  border: solid;
  cursor: pointer;
  position: relative;
  font-size: initial;
  width: fit-content;
  display: block;
  margin: 15px auto;
}
/* ボタン要素のホバーエフェクト */
.floaton-ukimasu:hover {
  transform: translateY(-5px);
  box-shadow: 0 4px 6px rgba(0, 0, 0, .5);
}

/* ホバー時に伸長するアンダーライン */
.header-sample .stretche-line {
  color: #4d5156;
  padding: 0 10px;
  text-decoration: none;
  line-height: 60px;
  display: block;
  position: relative;
}
.stretche-line::after {
  position: absolute;
  left: 0;
  content: '';
  width: 100%;
  height: 6px;
  background: #ffb300;
  bottom: 9px;
  transform: scale(0, 1);
  transform-origin: center top;
  transition: transform 0.3s;
  border-radius: 3px;
}
/* ホバー時のエフェクト */
.stretche-line:hover::after {
  transform: scale(.8, 1);
}

/* 矢印のスタイル設定 */
.ds-icon {
  display: block;
  position: relative;
  width: 24px;
  margin-left: 6px;
  flex-shrink: 0;
  transform-origin: center 43%;
  transition: transform 0.4s;
}
.ds-icon::before,.ds-icon::after {
  content: "";
  position: absolute;
  display: block;
  width: 15px;
  height: 3px;
  background-color: hotpink;
}
/* 矢印の右向き斜線 */
.ds-icon::after {
  right: 0;
  transform: rotate(-45deg);
}
/* 矢印の左向き斜線 */
.ds-icon::before {
  left: 0;
  transform: rotate(45deg);
}
/* 矢印を反転(summaryが開いている場合) */
details.is-opened .ds-icon {
  transform: rotate(180deg);
}

おわりに

アクセシビリティを重視したWebデザインにおいて、details・summaryタグとGSAPライブラリを組み合わせたアニメーション効果の導入はユーザーエクスペリエンス向上に寄与します。シンプルな操作性やスクリーンリーダーのサポート、検索との連携など、様々な側面からアクセシビリティを考慮しました。

これらの手法を取り入れることで、ウェブサイトやアプリの利用者がより快適かつ効果的に情報を取得できるでしょう。 Webデザインにおけるアクセシビリティの重要性を理解し、ぜひ実践してみてください。

湯気の出る屋台