😎

タブUI - コピペで使えるアクセシビリティ対応モジュール

に公開

はじめに

こんにちは。株式会社VOWZ の Chikara です。
弊社では、定期的なアクセシビリティ講習の実施や、制作したWebページに対するアクセシビリティチェックなど、企業として継続的にアクセシビリティに取り組んでいます。

今回は、タブUI に関する解説をお届けします。
タブUIは、ul 要素のリストや input type="checkbox" のような、HTML標準のセマンティクスが用意されていないため、div 要素などをカスタマイズして実装する必要があります。
モジュールだけを確認したい場合は、目次の【モジュール】をご参照ください。

基本編

【モジュール】タブUI - アニメーション無し

まずは実際の挙動を見ていただきます。

クリックすることによって目的のタブを選択することができます。
また、TABキーを押すことで「タブ1」がフォーカスされ、その後は矢印キーと決定キー(Enter または Space)で同様の操作が可能です。

HTMLの構成とアクセシビリティ対応

<!-- コンテンツ -->
<div class="tabs">
  <h3 id="tablist-1">タブリスト見出し</h3>
  <div role="tablist" aria-labelledby="tablist-1" class="manual">
    <button id="tab-1" type="button" role="tab" aria-selected="true" aria-controls="tabpanel-1">
      <span class="focus">タブ1</span>
    </button>
    <button id="tab-2" type="button" role="tab" aria-selected="false" aria-controls="tabpanel-2" tabindex="-1">
      <span class="focus">タブ2</span>
    </button>
    <button id="tab-3" type="button" role="tab" aria-selected="false" aria-controls="tabpanel-3" tabindex="-1">
      <span class="focus">タブ3</span>
    </button>
    <button id="tab-4" type="button" role="tab" aria-selected="false" aria-controls="tabpanel-4" tabindex="-1">
      <span class="focus">タブ4</span>
    </button>
  </div>

  <div id="tabpanel-1" role="tabpanel" aria-labelledby="tab-1">
    <p>これはタブ1のサンプルテキストです。</p>
  </div>
  <div id="tabpanel-2" role="tabpanel" aria-labelledby="tab-2" class="is-hidden">
    <p>これはタブ2のサンプルテキストです。</p>
  </div>
  <div id="tabpanel-3" role="tabpanel" aria-labelledby="tab-3" class="is-hidden">
    <p>これはタブ3のサンプルテキストです。</p>
  </div>
  <div id="tabpanel-4" role="tabpanel" aria-labelledby="tab-4" class="is-hidden">
    <p>これはタブ4のサンプルテキストです。</p>
  </div>
</div>

<div class="tabs">

この要素は、タブUI全体を包む親要素です。複数のタブボタンと、それに対応するタブパネル(コンテンツ)を内包します。


<h3 id="tablist-1">タブリスト見出し</h3>

タブグループのセクションを示す見出し要素です。id属性を用いることで、後述するタブリストとの関連付けが可能になります。


<div role="tablist" aria-labelledby="tablist-1" class="manual">

この部分がタブのボタンが並ぶリストになります。

  • role="tablist":この要素が「タブリスト」であることを、スクリーンリーダーに伝えます。
  • aria-labelledby="tablist-1":見出し(h3)と関連付けることで、「このタブリストはどんな内容のものか」を補足的に説明できます。

<button id="tab-1" type="button" role="tab" aria-selected="true" aria-controls="tabpanel-1">

個々のタブボタンを表す要素です。

  • role="tab":このボタンが「タブ」であることを明示します。
  • aria-selected="true":現在選択されているタブにはtrue、それ以外はfalseを設定します。
  • aria-controls="tabpanel-1":このタブが制御しているパネルのIDを指定します。

<div id="tabpanel-1" role="tabpanel" aria-labelledby="tab-1">

タブボタンに対応するコンテンツ領域(タブパネル)です。

  • role="tabpanel":このエリアがタブの内容部分であることを示します。
  • aria-labelledby="tab-1":どのタブボタンに対応しているかをスクリーンリーダーが理解できるようにします。

<div class="is-hidden">

非表示状態のタブパネルに付与されるクラスです。CSSとJavaScriptを使って表示・非表示を切り替えます。


role属性について

今回のタブUIでは、主に tablisttabtabpanel の3つの role 属性が使われています。
以下は構造を簡略化したコード例です。

<div role="tablist">
  <button role="tab" aria-selected="true" aria-controls="tabpanel-1" id="tab-1">タブ1</button>
  <button role="tab" aria-selected="false" aria-controls="tabpanel-2" id="tab-2">タブ2</button>
  <button role="tab" aria-selected="false" aria-controls="tabpanel-3" id="tab-3">タブ3</button>
</div>

<div role="tabpanel" id="tabpanel-1" aria-labelledby="tab-1">
  <p>タブ1の内容</p>
</div>
<div role="tabpanel" id="tabpanel-2" aria-labelledby="tab-2" hidden>
  <p>タブ2の内容</p>
</div>
<div role="tabpanel" id="tabpanel-3" aria-labelledby="tab-3" hidden>
  <p>タブ3の内容</p>
</div>

この例からもわかるように、tablist の中に tab が並び、それぞれのタブに対応する tabpanel は外側に配置されています。
アクセシビリティの観点では、以下のルールを押さえておくことが重要です。

  • role="tab"必ず role="tablist" の直接の子要素である必要があります。
  • role="tabpanel"tablist の中に含まれている必要はなく、aria-labelledby 属性で対応するタブと関連付けることで正しく機能します。

💡よくある間違いや注意点

次のように、tabdiv で囲んでしまうと、tablist の直接の子要素でなくなり、スクリーンリーダーが正しく認識できない可能性があります

<div role="tablist">
<div>
   <button role="tab" aria-controls="tabpanel-1" id="tab-1">タブ1</button>
 </div>
 <div>
   <button role="tab" aria-controls="tabpanel-2" id="tab-2">タブ2</button>
 </div>
</div>

応用編

【モジュール】タブUI - アニメーション①


【モジュール】タブUI - アニメーション②


基本編パーツとの違い

基本編で行われている処理

基本編で使用されているJavascriptでは以下の処理を行なっています。

this.tabpanels[i].classList.remove('is-hidden');
...
this.tabpanels[i].classList.add('is-hidden');
  • 表示すべきタブパネルからis-hiddenクラスを削除して表示。
  • 非表示にするパネルにはis-hiddenクラスを付与して非表示にします。
    • is-hiddenクラスは、CSSでdisplay: none;などの非表示スタイルを設定しています。

応用編で行われている処理

this.tabpanels[i].classList.add('is-active');
...
this.tabpanels[i].classList.remove('is-active');
  • 表示すべきタブパネルにis-activeクラスを追加。
  • それ以外のパネルからはis-activeクラスを除去します。
    • is-activeクラスは、CSSでdisplay: block;opacity: 1;などの表示用スタイルを与えます。

比較

項目 基本編 応用編
タブのデフォルトスタイル 表示 非表示
クラス付与後のスタイル 非表示(is-hidden 表示(is-active
クラスが付与される対象 表示されていないタブ 表示されているタブ
CSSの想定 display: none; display: block;, visibility, opacity

まとめ

タブUIは、Webサイトでも使用頻度の高いUIのひとつですが、実際に使われているものの中には、「キーボード操作ができない」「role属性などのマークアップがまったく行われていない」といった課題を抱えているケースも少なくありません。
今回ご紹介したモジュールについては、すべての支援技術での完全な動作保証まではできないものの、Chromeの拡張機能「axe DevTools」でのアクセシビリティチェックをクリアしたものを掲載しています。
アクセシブルなサイトづくりを目指すエンジニアの皆さまにとって、ささやかでも一助となれば幸いです。


※本記事で掲載されているモジュールは、W3Cによる ARIA Authoring Practices を引用・調整したものです。ライセンスは W3C Software License に準拠します。

GitHubで編集を提案

Discussion