Tab — タブ切り替え(コンテンツ切替・バッジ付き)

表示・インジケーター 初級

このコンポーネントについて

タブ(tab)は、限られたスペースに複数のコンテンツを切り替えて表示するUIです。プロフィール・投稿・設定のように関連性のある情報をまとめるのに向いており、メール・SNS・管理画面など幅広い場面で使われます。

このページでは、基本的なコンテンツ切替タブと、件数バッジ付きタブの2パターンを紹介します。どちらもバニラJSのみで実装しており、複数のタブグループが同一ページに存在しても独立して動作します。

  • 基本タブ — ボタンクリックでパネルを切り替え。アクティブタブは下線とカラーで強調表示
  • バッジ付きタブ — 各タブに件数を示すバッジを表示。未読・新着は赤バッジで強調
  • アクセシビリティ対応role="tablist" / role="tab" / aria-selected を付与
  • 複数グループ対応 — 同ページ内に複数のタブグループを設置しても互いに干渉しない

実装のポイント・注意点

タブの切り替えは display: nonedisplay: blockclassList で制御しています。hidden 属性を使う方法もありますが、CSSで .tab-panel { display: none } / .tab-panel.active { display: block } と書く方がよく使われ、スタイルとロジックを分離しやすいです。

data-tabdata-panel を使ってボタンとパネルを紐付けています。btn.dataset.tab で取得した値を querySelector('.tab-panel[data-panel="..."]') で使うことで、ボタンとパネルのHTMLの並び順が一致していなくても正しく動きます。

アクセシビリティのため role="tablist"role="tab"aria-selected="true/false" を付与しています。スクリーンリーダーがタブUIとして認識できるようになります。キーボード操作(矢印キーでタブ移動)まで実装するには keydown イベントも追加が必要ですが、このサンプルは基本的なマウス操作のみ対応しています。

HTML・CSS・バニラJavaScriptのみで実装しており、フレームワーク不要でコピペすぐに動きます。

デモ

Pattern 1 — 基本タブ

🧑

山田 太郎

UIデザイナー / フロントエンドエンジニア

  • CSSでタブUIを実装する方法

  • バニラJSでモーダルを作る

  • レスポンシブデザインのコツ

  • 👩
    鈴木 花子
  • 👨
    田中 一郎
  • 🧑
    佐藤 次郎

Pattern 2 — バッジ付きタブ(件数表示)

  • 【重要】パスワードのリセットが完了しました

    パスワードのリセット処理が完了しました。ご不明な点は...

  • 新機能のお知らせ

    この度、新しい機能を追加いたしました。ぜひお試し...

  • ご利用ありがとうございます

    先日はご利用いただきありがとうございました。今後も...

送信済みメッセージはありません

  • (無題)

    先日の件についてですが...

  • お問い合わせへの返信

    ご連絡いただきありがとうございます。

サンプルソース

3つのファイルを同じフォルダに保存し、index.html をブラウザで開くとすぐに動作確認できます。
ファイル名:index.html / style.css / script.js — 保存時の文字コードは UTF-8 を指定してください(Shift-JISだと日本語が文字化けします)。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>タブ切り替えサンプル</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>

<div class="tab-root">
  <div class="tab-nav" role="tablist">
    <button class="tab-btn active" role="tab" aria-selected="true"  data-tab="tab1">タブ1</button>
    <button class="tab-btn"        role="tab" aria-selected="false" data-tab="tab2">タブ2</button>
    <button class="tab-btn"        role="tab" aria-selected="false" data-tab="tab3">タブ3</button>
  </div>
  <div class="tab-panel active" data-panel="tab1">
    <p>タブ1のコンテンツ</p>
  </div>
  <div class="tab-panel" data-panel="tab2">
    <p>タブ2のコンテンツ</p>
  </div>
  <div class="tab-panel" data-panel="tab3">
    <p>タブ3のコンテンツ</p>
  </div>
</div>

<script src="./script.js"></script>
</body>
</html>
:root {
  --tab-primary: #2B7FE8;
  --tab-border:  #E5E9EF;
  --tab-radius:  8px;
}

*, *::before, *::after { box-sizing: border-box; }

body {
  font-family: sans-serif;
  padding: 24px;
  color: #1A2332;
}

/* タブ全体のコンテナ */
.tab-root {
  border: 1.5px solid var(--tab-border);
  border-radius: var(--tab-radius);
  overflow: hidden;
  max-width: 520px;
}

/* タブナビゲーション */
.tab-nav {
  display: flex;
  border-bottom: 1.5px solid var(--tab-border);
  background: #F8FAFC;
}

/* タブボタン */
.tab-btn {
  flex: 1;
  padding: 12px 16px;
  background: none;
  border: none;
  border-bottom: 3px solid transparent;
  font-size: 14px;
  font-family: sans-serif;
  color: #5A6A7A;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
  font-weight: 500;
}

.tab-btn:hover { color: var(--tab-primary); }

/* アクティブなタブ */
.tab-btn.active {
  color: var(--tab-primary);
  border-bottom-color: var(--tab-primary);
  font-weight: 700;
  background: #fff;
}

/* タブパネル */
.tab-panel {
  display: none;
  padding: 20px;
}

.tab-panel.active { display: block; }
var tabBtns   = document.querySelectorAll('.tab-btn');
var tabPanels = document.querySelectorAll('.tab-panel');

// タブボタンごとにクリックイベントを設定
tabBtns.forEach(function(btn) {
  btn.addEventListener('click', function() {
    var target = btn.dataset.tab;

    // すべてのボタン・パネルの active を解除
    tabBtns.forEach(function(b) {
      b.classList.remove('active');
      b.setAttribute('aria-selected', 'false');
    });
    tabPanels.forEach(function(p) { p.classList.remove('active'); });

    // クリックされたタブを active に
    btn.classList.add('active');
    btn.setAttribute('aria-selected', 'true');
    document.querySelector('.tab-panel[data-panel="' + target + '"]').classList.add('active');
  });
});

AI用プロンプト

以下のプロンプトをコピーしてAIに渡すと、同様のコンポーネントを生成できます。

ChatGPTやClaudeにこのプロンプトを渡すと、同様のコンポーネントをゼロから生成・カスタマイズできます。タブ数の変更やコンテンツの差し替えなど、要件を追記して使うのがおすすめです。

※ このプロンプトを使ってもデモとまったく同じ動作にならない場合があります。AIの解釈や生成タイミングによって差が出ることをご了承ください。

💡 jQuery・Vue・React など特定のライブラリで実装したい場合は、プロンプトの末尾に「〇〇を使って実装してください」と追記してください。

# タブ切り替え 作成依頼

## 概要
クリックでコンテンツを切り替えるタブUIを実装してください。

## 要件
- タブボタンをクリックすると対応するパネル(コンテンツ領域)に切り替わる
- アクティブなタブはボタン下部に色付きの下線を表示して強調する
- 非アクティブなパネルは非表示にする(display: none)
- タブの数は3つとし、各タブに適切なサンプルコンテンツを表示する
- アクセシビリティのため role="tablist" / role="tab" / aria-selected を付与する

## 技術仕様
- HTML / CSS / バニラJavaScript で実装
- 外部ライブラリ:なし
- レスポンシブ対応:必要

## 動作詳細
タブボタンはナビゲーション行に横並びに配置し、クリックすると対応パネルが表示される。
ボタンと対応パネルは data-tab / data-panel 属性で紐付ける。
アクティブなタブにはクラス active を付与し、CSS で下線・文字色・背景色を変える。
タブボタン全体を querySelectorAll で取得し、forEach でイベントを設定する。
クリック時にすべてのボタン・パネルの active を解除してから、対象だけに active を付与する。

## 出力形式
HTML・CSS・JavaScriptを分けて出力してください。
各ファイルは単独でコピー&ペーストして使えるよう記述してください。