バッジ(件数・通知表示)

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

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

バッジは、アイコンやボタンの右上に小さく重ねて表示するUI要素です。 未読メッセージ数・未処理タスク数・通知の有無など、「ユーザーがすぐ気づくべき情報」を示すために業務アプリで広く使われます。

このページでは、件数を数字で表示するタイプ・ボタン操作で件数を動的に増減するタイプ・件数なしで「未読あり」を示す赤丸ドットの3パターンを確認・コピペできます。

  • 数字バッジ(ナビアイコン) — アイコン右上に件数を表示する定番パターン。100件以上は「99+」に切り替えてレイアウト崩れを防ぐ
  • インタラクティブ(件数増減) — ボタンで件数をリアルタイムに操作でき、0件でバッジが消える・100件超で「99+」になる動作を確認できる
  • ドットバッジ — 件数を表示せず「未読あり」の有無だけを示す赤丸。アイコン・ボタン・アバターの3パターンを掲載

実装のポイント・注意点

バッジの配置は position: absolute で実装します。バッジを重ねたい要素の親に position: relative; display: inline-flex; を設定することがポイントです。position: relative がないとバッジが意図しない位置にずれるため、必ずアンカー要素に付けてください。

数字バッジの幅は件数によって変わるため、min-widthpadding の両方を設定して最小サイズを確保します。「99+」の文字幅は数字2桁より広いため、この対処が特に重要です。バッジの非表示は JavaScript 側で el.hidden = true を使うと確実です。

件数変化時にバッジをスケールアニメーションさせると、変化に気づきやすくなります。同じ要素のアニメーションを再実行するには、クラスを外したあと void el.offsetWidth でブラウザに強制レイアウト計算をさせてからクラスを付与し直すテクニックを使います。

デモ

Pattern 1 — 数字バッジ(ナビアイコン)

3
メール
🔔 12
通知
99+
タスク

Pattern 2 — インタラクティブ(件数増減)

🔔 5

現在: 5件

Pattern 3 — ドットバッジ(未読あり表示)

アイコン
ボタン
アバター

サンプルソース

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="bdg-nav-bar">
  <div class="bdg-nav-item">
    <div class="bdg-icon-wrap">
      <span class="bdg-icon-box">✉</span>
      <span class="bdg-count">3</span>
    </div>
    <span class="bdg-nav-label">メール</span>
  </div>
  <div class="bdg-nav-item">
    <div class="bdg-icon-wrap">
      <span class="bdg-icon-box">🔔</span>
      <span class="bdg-count">12</span>
    </div>
    <span class="bdg-nav-label">通知</span>
  </div>
  <!-- 100件超のとき「99+」と表示する -->
  <div class="bdg-nav-item">
    <div class="bdg-icon-wrap">
      <span class="bdg-icon-box">☑</span>
      <span class="bdg-count">99+</span>
    </div>
    <span class="bdg-nav-label">タスク</span>
  </div>
</div>

<!-- ========================================
  インタラクティブ(件数増減)
  ボタンで件数をリアルタイムに変化させる
======================================== -->
<div class="bdg-interactive">
  <div class="bdg-icon-wrap bdg-icon-wrap--lg">
    <span class="bdg-icon-box bdg-icon-box--lg">🔔</span>
    <span class="bdg-count" id="js-badge">5</span>
  </div>
  <div class="bdg-controls">
    <button id="btn-increment" type="button">+1</button>
    <button id="btn-decrement" type="button">−1</button>
    <button id="btn-clear" type="button">クリア</button>
  </div>
  <p class="bdg-count-display" id="count-display">現在: 5件</p>
</div>

<!-- ========================================
  ドットバッジ
  件数なしで「未読あり」を示す赤丸
======================================== -->
<div class="bdg-dot-row">
  <div class="bdg-dot-item">
    <div class="bdg-icon-wrap">
      <span class="bdg-icon-box">✉</span>
      <span class="bdg-dot"></span>
    </div>
    <span class="bdg-dot-label">アイコン</span>
  </div>
  <div class="bdg-dot-item">
    <div class="bdg-btn-wrap">
      <button class="bdg-btn" type="button">メッセージ</button>
      <span class="bdg-dot"></span>
    </div>
    <span class="bdg-dot-label">ボタン</span>
  </div>
  <div class="bdg-dot-item">
    <div class="bdg-icon-wrap">
      <span class="bdg-avatar">山</span>
      <span class="bdg-dot"></span>
    </div>
    <span class="bdg-dot-label">アバター</span>
  </div>
</div>

<script src="./script.js"></script>
</body>
</html>
/* バッジ パターン集 — style.css
   色を変えたいときは :root の変数を書き換えるだけでOK */
:root {
  --badge-bg:    #E53E3E;   /* バッジ背景色(赤) */
  --badge-color: #ffffff;   /* バッジ文字色 */
  --icon-bg:     #F4F6F9;   /* アイコンボックス背景 */
  --icon-border: #E5E7EB;   /* アイコンボックスのボーダー */
  --text-color:  #1A2332;   /* メインテキスト色 */
  --muted-color: #5A6A7A;   /* サブテキスト色 */
  --accent:      #2B7FE8;   /* ボタン・アバター等のアクセント */
}

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

body {
  font-family: sans-serif;
  padding: 24px;
  max-width: 480px;
  margin: 0 auto;
  background: #fff;
  color: var(--text-color);
}

/* ========== バッジ 共通 ========== */

/* バッジを右上に重ねるためのアンカー要素 */
.bdg-icon-wrap {
  position: relative;
  display: inline-flex;
}

/* 数字バッジ */
.bdg-count {
  position: absolute;
  top: -6px;
  right: -8px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: 9999px;
  background: var(--badge-bg);
  color: var(--badge-color);
  font-size: 11px;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
  white-space: nowrap;
}

/* 件数が0のとき hidden 属性で非表示にする */
.bdg-count[hidden] { display: none; }

/* ドットバッジ(件数なし・未読あり表示) */
.bdg-dot {
  position: absolute;
  top: -3px;
  right: -3px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--badge-bg);
  /* 背景と同色の縁でバッジをアイコンから視覚的に分離する */
  border: 2px solid #fff;
}

/* アイコンボックス */
.bdg-icon-box {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  background: var(--icon-bg);
  border: 1.5px solid var(--icon-border);
  border-radius: 12px;
  font-size: 22px;
}

/* アバター(イニシャルを丸く表示する) */
.bdg-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-size: 18px;
  font-weight: 700;
}

/* ========== Pattern 1: 数字バッジ(ナビアイコン) ========== */

.bdg-nav-bar {
  display: flex;
  gap: 16px;
  margin-bottom: 40px;
}

.bdg-nav-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
}

.bdg-nav-label {
  font-size: 11px;
  color: var(--muted-color);
}

/* ========== Pattern 2: インタラクティブ ========== */

.bdg-interactive {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 16px;
  margin-bottom: 40px;
}

/* 大きいアイコンボックス */
.bdg-icon-box--lg {
  width: 64px;
  height: 64px;
  font-size: 28px;
  border-radius: 16px;
}
.bdg-icon-wrap--lg .bdg-count {
  top: -8px;
  right: -10px;
  min-width: 22px;
  height: 22px;
  font-size: 12px;
  line-height: 22px;
}

.bdg-controls {
  display: flex;
  gap: 8px;
}

.bdg-controls button {
  padding: 8px 16px;
  border-radius: 6px;
  border: 1.5px solid var(--icon-border);
  background: #fff;
  font-size: 14px;
  cursor: pointer;
  font-family: sans-serif;
  transition: background 0.15s;
}
.bdg-controls button:hover { background: var(--icon-bg); }

.bdg-count-display {
  font-size: 13px;
  color: var(--muted-color);
  margin: 0;
}

/* バッジ更新アニメーション */
@keyframes bdg-pop {
  0%   { transform: scale(1); }
  50%  { transform: scale(1.4); }
  100% { transform: scale(1); }
}
.bdg-count.is-popping {
  animation: bdg-pop 0.2s ease;
}

/* ========== Pattern 3: ドットバッジ ========== */

.bdg-dot-row {
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
  margin-bottom: 40px;
}

.bdg-dot-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.bdg-dot-label {
  font-size: 12px;
  color: var(--muted-color);
}

/* ボタン + ドットバッジ */
.bdg-btn-wrap {
  position: relative;
  display: inline-flex;
}

.bdg-btn {
  padding: 10px 16px;
  border-radius: 8px;
  border: 1.5px solid var(--icon-border);
  background: var(--icon-bg);
  font-size: 14px;
  font-family: sans-serif;
  cursor: default;
}

/* スマホ対応 */
@media (max-width: 480px) {
  body { padding: 16px; }
  .bdg-dot-row { gap: 16px; }
}
// === インタラクティブ件数増減 ===
var badgeEl   = document.getElementById('js-badge');
var displayEl = document.getElementById('count-display');
var count     = 5; // 初期件数

// 件数に応じてバッジの表示を更新する
function updateBadge() {
  if (count <= 0) {
    // 0件: バッジを非表示にする
    badgeEl.hidden = true;
    displayEl.textContent = '現在: 0件(バッジ非表示)';
    return;
  }
  badgeEl.hidden = false;
  // 100件以上: 「99+」と表示する
  badgeEl.textContent = count > 99 ? '99+' : String(count);
  displayEl.textContent = '現在: ' + count + '件';

  // アニメーションを再生する
  // クラスを外してから offsetWidth を参照してブラウザに強制レイアウト計算させ、
  // アニメーションをリセットしてから付与し直す
  badgeEl.classList.remove('is-popping');
  void badgeEl.offsetWidth;
  badgeEl.classList.add('is-popping');
}

document.getElementById('btn-increment').addEventListener('click', function () {
  count++;
  updateBadge();
});

document.getElementById('btn-decrement').addEventListener('click', function () {
  if (count > 0) count--;
  updateBadge();
});

document.getElementById('btn-clear').addEventListener('click', function () {
  count = 0;
  updateBadge();
});

AI用プロンプト

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

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

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

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

Pattern 1 & 3 — 静的バッジ表示

# バッジ(数字バッジ・ドットバッジ)作成依頼

## 概要
アイコンやボタンの右上に重ねて表示する通知バッジを実装してください。
件数を数字で示すタイプと、件数なしで「未読あり」を示すドットタイプの2種類を作成します。

## 要件
- アイコンの右上に件数バッジを重ねて表示する
- 件数が99を超える場合は「99+」と表示する
- 件数が0の場合はバッジを非表示にする
- 件数なしの「ドットバッジ」(赤丸のみ)も作成する
- アイコン・ボタン・アバターの3パターンにドットバッジを付ける

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

## 動作詳細
バッジは position: absolute でアイコン右上に重ねて表示する。
親要素に position: relative と display: inline-flex を付けること。
数字バッジ(.bdg-count)は min-width: 18px・height: 18px の丸形で背景色は赤(#E53E3E)、文字色は白。
ドットバッジ(.bdg-dot)は width: 10px・height: 10px の赤丸で数字は表示しない。
件数0でバッジを hidden 属性にする処理は JavaScript で行う。

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

Pattern 2 — インタラクティブ(件数増減)

# バッジ(インタラクティブ件数増減)作成依頼

## 概要
ボタン操作でバッジの件数をリアルタイムに増減できるデモを実装してください。
未読件数が変動する業務アプリのナビゲーション表示を想定しています。

## 要件
- ベルアイコンの右上に件数バッジを表示する(初期値: 5)
- 「+1」「−1」「クリア」の3ボタンで件数を操作できる
- 件数が0になるとバッジが非表示になる
- 件数が100以上になると「99+」と表示する
- 件数変化時にバッジにスケールアニメーションを付ける
- バッジ下部に現在の件数テキストを表示する

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

## 動作詳細
let count = 5 を初期値とし、各ボタンのクリックで count を変化させる。
バッジ要素のテキスト更新は textContent を使う(innerHTML 禁止)。
件数が0のとき badge 要素に hidden 属性を付与して非表示にする。
アニメーション再実行は、クラスを外してから void el.offsetWidth でレイアウトをリセットし、クラスを付与し直す。

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