コピペで完結!タブ切替 #12【タブ内を集計してバッジで表示できるタブ】

html/css/js

タブ内を集計してバッジで表示できるタブ

各タブに対応するパネルの中身を集計し、その件数をタブ横のバッジに表示する仕組みです。 未読数やタスク件数など、動的に変化する情報をタブで直感的に確認できるUIとして活用できます。 コードはARIA対応済みでアクセシブル、コピペですぐに利用可能です。

  • パネル内の要素数を自動で集計してタブに反映
  • 未読数・通知数・タスク件数などに応用可能
  • ARIA属性でアクセシブル設計
  • コピペで導入できるシンプルな実装
コードについて 本記事のコードは AI(ChatGPT)による生成をベースに作成・調整しています。ご利用の環境でテストの上ご使用ください。
免責 本コードの利用に伴う不具合・損害について、当サイトは責任を負いません。自己責任にてご利用ください。

デモ

新機能をリリースしました。
メンテナンスのお知らせ。
アップデートのご案内。

※ボタン操作で「未読→既読」にし、バッジ件数を動的更新するのは[コピペ用JS]で解説。

コードをコピーして使おう!

/* =========================================
   バッジ/件数表示つきタブ:CSS(フルコメント版)
   -----------------------------------------
   目的:
     - タブのラベル右側に「件数バッジ」を表示して視認性を高める
     - クリックやキーボード操作でのタブ切替に併せ、見た目を明確化
     - アクセシビリティ配慮(表示/非表示は JS で hidden を制御)
   方針:
     - 範囲衝突を避けるため #badge-section 配下のみをスタイリング
     - 色やサイズはカスタムプロパティ(CSS変数)で柔軟に調整可能
   備考:
     - バッジ値は静的/動的いずれでもOK(JSで更新する想定)
   ========================================= */

/* -----------------------------------------
   #badge-section(コンポーネントのルート)
   - 色・タイポ・レイアウト幅の基本設定
   - カスタムプロパティで配色を集中管理
------------------------------------------ */
#badge-section{
  --ink:#0f172a;   /* 文字色(本文の基本色) */
  --muted:#64748b; /* 補助文字(説明文・注釈など) */
  --accent:#0b6bff;/* アクティブ強調色(選択タブなど) */
  --border:#e5e7eb;/* 枠線色(薄いグレー) */
  --panel:#ffffff; /* パネル背景色(白) */
  --badge:#ef4444; /* バッジ背景色(赤系) */
  --badge-ink:#fff;/* バッジ文字色(白) */

  color:var(--ink); /* セクション内の既定文字色を設定 */
  font-family: system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Noto Sans JP","Yu Gothic UI",sans-serif; /* 可読性の高いシステムフォント */
  max-width: 820px; /* セクション自体の最大幅(レイアウトを保つ) */
  margin: 0 auto;   /* 中央寄せ(左右の余白を同等に) */
}

/* -----------------------------------------
   .tabs(タブバーのコンテナ)
   - 横並び・折り返し対応
   - 下地や枠線でタブ群を視覚的に囲う
------------------------------------------ */
#badge-section .tabs{
  display:flex;            /* タブを横並びに配置 */
  gap:8px;                 /* タブ同士の間隔を確保 */
  padding:8px;             /* タブバー内側の余白 */
  background:#f8fafc;      /* 薄い背景で領域を区切る */
  border:1px solid var(--border); /* 外枠の線でまとまりを出す */
  border-radius:12px;      /* 角丸で柔らかい印象に */
  flex-wrap:wrap;          /* 画面幅が狭い場合は折返して崩れを防ぐ */
}

/* -----------------------------------------
   .tab(個々のタブ=ボタン)
   - ピル型(pill)でバッジと横並び
   - ホバー/アクティブ時の違いは最小限
------------------------------------------ */
#badge-section .tab{
  appearance:none;         /* ブラウザ既定のボタン装飾を解除 */
  background:#fff;         /* タブの背景は白 */
  color:var(--ink);        /* 文字色は基本色 */
  border:1px solid var(--border); /* 薄い枠線でボタン感を出す */
  border-radius:999px;     /* ピル形状(太い角丸) */
  padding:8px 12px;        /* クリックしやすい余白 */
  font-size:14px;          /* 読みやすい文字サイズ */
  line-height:1;           /* バッジとの高さ揃えを取りやすく */
  cursor:pointer;          /* インタラクティブであることを示す */
  display:inline-flex;     /* テキストとバッジを横並びに */
  gap:8px;                 /* テキストとバッジの間隔 */
  align-items:center;      /* 縦位置を中央に揃える */
  transition: background .2s, border-color .2s, color .2s, transform .05s; /* 状態変化を滑らかに */
}

/* -----------------------------------------
   .tab:active(クリック中のわずかな押下感)
------------------------------------------ */
#badge-section .tab:active{
  transform: translateY(1px); /* 1px 下げて物理的な押下感を演出 */
}

/* -----------------------------------------
   .tab[aria-selected="true"](選択タブの強調)
   - アクセシビリティ属性に連動した見た目
------------------------------------------ */
#badge-section .tab[aria-selected="true"]{
  background:var(--accent);   /* 背景を強調色に */
  border-color:var(--accent); /* 枠線も強調色で統一 */
  color:#fff;                 /* 文字色は白でコントラスト確保 */
}

/* -----------------------------------------
   .badge(件数ラベル)
   - 最小幅を設けて1桁でも丸く見えるよう調整
   - 2桁以上でも収まるよう横方向の余白を用意
------------------------------------------ */
#badge-section .badge{
  min-width:20px;        /* 1桁でも丸形を維持できる幅 */
  height:20px;           /* 高さ(正円に近づける) */
  padding:0 6px;         /* 2桁以上の幅にも対応 */
  border-radius:999px;   /* 完全な丸み */
  background:var(--badge);     /* バッジの背景色(赤) */
  color:var(--badge-ink);      /* バッジ内の文字色(白) */
  display:inline-flex;         /* 中央寄せのためにフレックス化 */
  align-items:center;          /* 垂直方向の中央寄せ */
  justify-content:center;      /* 水平方向の中央寄せ */
  font-weight:700;             /* 数字を視認しやすく太字に */
  font-size:12px;              /* 小さめでも読めるサイズ */
}

/* -----------------------------------------
   .panel(タブと対応する内容領域)
   - カード風に枠線と角丸、十分な内側余白
------------------------------------------ */
#badge-section .panel{
  border:1px solid var(--border); /* 薄い枠線で領域を区切る */
  border-radius:12px;             /* 優しい角丸 */
  background:var(--panel);        /* 背景は白 */
  padding:16px;                   /* 読みやすい内側余白 */
  line-height:1.9;                /* 行間を広めにして可読性UP */
}

/* -----------------------------------------
   [role="tabpanel"][hidden]
   - JS 側で hidden を付与/除去して表示切替
   - display:none; で読み上げ対象にもならない(多くのスクリーンリーダでOK)
------------------------------------------ */
#badge-section [role="tabpanel"][hidden]{
  display:none; /* 非表示(レイアウトからも除外) */
}

/* -----------------------------------------
   デモ用スタイル(.item / .dot / .muted)
   - 実運用では任意のDOM構造でOK(ここでは未読の丸点などを表現)
------------------------------------------ */
#badge-section .item{
  display:flex;              /* アイコンとテキストを横並び */
  align-items:center;        /* 縦位置の中央そろえ */
  gap:10px;                  /* アイコンとテキスト間の余白 */
}
#badge-section .dot{
  width:8px; height:8px;     /* 小さな丸点のサイズ */
  border-radius:999px;       /* 完全な円形 */
  background:#22c55e;        /* 緑の丸点(未読・アクティブの例) */
}
#badge-section .muted{
  color:var(--muted);        /* 補助的な説明文の色 */
  font-size:13px;            /* 本文より少し小さいサイズ */
}

コメント