TypeScriptで通知バッジを実装する:ベルアイコンに数字を表示する
SNSやチャットアプリでよく見る、ベルアイコン右上に数字を表示する通知バッジの実装です。ライブラリなしの HTML + CSS + TypeScript で作れます。

HTML構造
position: relative の親要素に対して、バッジを position: absolute で重ねます。
<div class="bell-wrapper"> <svg class="bell-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"> <path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"/> </svg> <span class="badge" id="badge"></span> </div>
CSS:バッジの配置とアニメーション
.bell-wrapper { position: relative; display: inline-block; } .badge { position: absolute; top: 0; right: 0; background: #e53935; color: white; font-size: 15px; font-weight: 700; min-width: 26px; height: 26px; border-radius: 13px; padding: 0 7px; display: flex; align-items: center; justify-content: center; border: 2px solid white; /* アイコンとの境界を明確にする */ opacity: 0; transform: scale(0); transition: opacity 0.15s, transform 0.15s; } .badge.visible { opacity: 1; transform: scale(1); } .badge.pop { animation: badge-pop 0.25s ease-out; } @keyframes badge-pop { 0% { transform: scale(1); } 50% { transform: scale(1.4); } 100% { transform: scale(1); } }
min-width を使うことで、1桁は円形、2桁以上は横長の丸角矩形になります。
TypeScript:カウントの更新
const badge = document.getElementById('badge') as HTMLSpanElement let count = 0 function updateBadge(): void { if (count === 0) { badge.classList.remove('visible') badge.textContent = '' return } badge.textContent = count > 9 ? '9+' : String(count) badge.classList.add('visible') // クラスを一度外してから付け直すことでアニメーションをリセット badge.classList.remove('pop') void badge.offsetWidth badge.classList.add('pop') } // 通知を追加 function addNotification(): void { count++ updateBadge() } // 通知をクリア(ベルクリック・クリアボタンどちらでも呼ぶ) function clearNotifications(): void { count = 0 updateBadge() }
void badge.offsetWidth は DOM の再描画を強制してアニメーションをリセットするイディオムです。これがないと、連続クリック時に pop アニメーションが再生されません。
イベントの登録
const bellWrapper = document.querySelector<HTMLElement>('.bell-wrapper')! const btnAdd = document.getElementById('btn-add') as HTMLButtonElement const btnClear = document.getElementById('btn-clear') as HTMLButtonElement bellWrapper.addEventListener('click', clearNotifications) btnAdd.addEventListener('click', addNotification) btnClear.addEventListener('click', clearNotifications)
実際のアプリではベルクリック時に通知一覧へ遷移しますが、clearNotifications() を呼ぶ位置は同じです。
10件超の表示
badge.textContent = count > 9 ? '9+' : String(count)
上限を変えたい場合は 9 の部分を変えるだけです。99+ にするなら count > 99 ? '99+' : String(count) にします。
まとめ
| やること | コード |
|---|---|
| バッジの配置 | 親に position: relative、バッジに position: absolute; top: 0; right: 0 |
| 表示・非表示 | visible クラスで opacity と transform: scale を切り替える |
| 出現アニメーション | @keyframes + クラスの付け外しでリセット |
| 10件超の表示 | count > 9 ? '9+' : String(count) |
| アニメーションリセット | void badge.offsetWidth で強制再描画 |