タブ・ツールバー

タブ

解説あり

Tabs

同じ領域で内容を切り替える。矢印キー移動・aria-selected・roving tabindex。

タブ(Tabs)とは?

タブは、同じ場所に複数の「パネル(内容のかたまり)」を重ねて置き、 上部の見出し(タブ)を選ぶことで表示するパネルを切り替える UI です。 設定画面、商品詳細、ダッシュボードなど「関連する情報をスペースを節約して並べたい」場面でよく使われます。

見た目は「ボタンが並んでいるだけ」ですが、支援技術には「これはタブの集まりで、いまどれが選ばれているか」を正しく伝える必要があります。

なぜアクセシビリティが大事なの?

ポイントは 「タブ全体で1タブストップ(roving tabindex)」aria-selected で選択状態を伝える」の2つです。

ライブデモ(推奨実装)

下のタブは APG に沿った実装です。マウスを使わずキーボードで操作してみてください。

アクセシブルなタブ

表示名やアイコンなど、公開プロフィールを編集します。

試してみよう:Tab で選択中のタブに入る → ← → でタブを移動(同時にパネルも切替)→ Home / End で最初・最後へ → もう一度 Tab でパネルへ。

ポイント

スクリーンリーダーでタブにフォーカスすると「プロフィール, タブ, 選択済み, 3個中1個目」のように役割・選択状態・位置が読み上げられます。これが role="tab"aria-selected の効果です。

キーボード操作

キー動作必須/任意
Tabタブリストに入る/出る(リスト全体で1タブストップ)必須
/ 前 / 次のタブへ移動(自動アクティベーションでは同時に選択)必須
Home / End最初 / 最後のタブへ移動任意(推奨)
Enter / Space手動アクティベーション時にフォーカス中のタブを選択任意

補足

パネルの中身がすぐ表示できる軽い内容なら、矢印キーで移動と同時に選択する 「自動アクティベーション」が快適です(このデモもそれ)。表示に通信や重い処理が伴う場合は、Enter/Space で確定する「手動アクティベーション」にします。

必要な WAI-ARIA / ロール

付ける場所属性 / ロール意味
タブを囲む箱role="tablist" + aria-labelタブの集まりであることと、その名前を伝える。
各タブrole="tab"タブであることを伝える。中身は <button> 推奨。
各タブaria-selected="true | false"いま選ばれているタブはどれか。切替時に必ず更新する。
各タブaria-controls="パネルのid"そのタブが制御するパネルを示す。
各タブtabindex="0 | -1"選択タブだけ 0、他は -1(roving tabindex)。
各パネルrole="tabpanel" + aria-labelledby="タブのid"対応するタブを名前として持つパネル。
各パネルtabindex="0"パネル内にフォーカス可能要素がない場合でも、パネル自体に Tab で入れるようにする。
非表示パネルhidden選ばれていないパネルは隠し、読み上げ/操作の対象外にする。

実装:推奨パターン(Good)

良い例 / 推奨

タブは <button role="tab">、パネルは role="tabpanel"。 選択状態を aria-selected と roving tabindex で同期させます。

マークアップ:

<div class="tabs">
  <div role="tablist" aria-label="アカウント設定">
    <button type="button"
            role="tab"
            id="tabs-tab-profile"
            aria-selected="true"
            aria-controls="tabs-panel-profile"
            tabindex="0">プロフィール</button>
    <button type="button"
            role="tab"
            id="tabs-tab-security"
            aria-selected="false"
            aria-controls="tabs-panel-security"
            tabindex="-1">セキュリティ</button>
  </div>

  <div role="tabpanel"
       id="tabs-panel-profile"
       aria-labelledby="tabs-tab-profile"
       tabindex="0">プロフィールの内容…</div>
  <div role="tabpanel"
       id="tabs-panel-security"
       aria-labelledby="tabs-tab-security"
       tabindex="0"
       hidden>セキュリティの内容…</div>
</div>

切替のスクリプト(aria-selected・tabindex・hidden を1か所で同期):

document.querySelectorAll('[data-tabs]').forEach((root) => {
  const tabs = Array.from(root.querySelectorAll('[role="tab"]'));

  function select(tab) {
    tabs.forEach((t) => {
      const selected = t === tab;
      // aria-selected と roving tabindex を同期させる
      t.setAttribute('aria-selected', String(selected));
      t.tabIndex = selected ? 0 : -1; // 選択タブだけ Tab で入れる
      const panel = document.getElementById(t.getAttribute('aria-controls'));
      if (panel) panel.hidden = !selected;
    });
  }

  tabs.forEach((tab, i) => {
    tab.addEventListener('click', () => select(tab));
    tab.addEventListener('keydown', (e) => {
      let next = null;
      if (e.key === 'ArrowRight') next = (i + 1) % tabs.length;
      else if (e.key === 'ArrowLeft') next = (i - 1 + tabs.length) % tabs.length;
      else if (e.key === 'Home') next = 0;
      else if (e.key === 'End') next = tabs.length - 1;
      if (next !== null) {
        e.preventDefault();
        tabs[next].focus(); // フォーカス移動
        select(tabs[next]); // 同時にそのタブを選択(自動アクティベーション)
      }
    });
  });
});

アンチパターン(Bad)

下は <div>onclick だけで作ったタブです。マウスでは切り替わりますが、キーボードでは一切操作できません。

div で作った壊れたタブ
プロフィール
セキュリティ
通知

表示名やアイコンなど、公開プロフィールを編集します。

試してみよう:Tab を押してもタブにフォーカスが当たらず、← → でも切り替わりません。スクリーンリーダーは「タブ」とも「選択中」とも認識できません。

<!-- ❌ アンチパターン -->
<div class="tabs">
  <div class="tablist">
    <!-- div + onclick。role も aria-selected もキーボードもない -->
    <div class="tab" onclick="show('p1')">プロフィール</div>
    <div class="tab" onclick="show('p2')">セキュリティ</div>
  </div>
  <div id="p1">プロフィールの内容…</div>
  <div id="p2" style="display:none">セキュリティの内容…</div>
</div>

悪い例 / 避ける

この実装の問題点:

  • キーボードで操作できないdiv はフォーカスを受け取れず、矢印キーも効かない。
  • タブだと伝わらないrole="tablist"/role="tab" がなく、ただのテキストに聞こえる。
  • 選択状態が伝わらないaria-selected がなく、いまどれが開いているか分からない。
  • パネルと関連付いていないaria-controls / aria-labelledby がない。

実装チェックリスト


原文(英語):Tabs Pattern — W3C APG(新しいタブで開きます)