Overview
Notification badges bounce when the count changes. The trick is keying the badge DOM node to the current count value so the framework (or, in vanilla JS, an explicit node clone) remounts the badge on every change — a fresh node has no in-flight animation, so the keyframe runs from frame 0 every time. Three variants ship: a subtle inbox shoulder-pop, a confident cart purchase-pop with rotate, and a tall alerts overshoot bell.
When to use it
Reach for the badge bounce on inbox icons, cart counters, alert bells, any persistent badge whose count changes. Skip it for badges that update frequently (every second) — the constant bouncing is distracting. Skip it on badges that already pulse for other reasons (live indicators); the two motions fight each other.
How it works
In React, set <Badge key={count}>{count}</Badge> — when count changes, React unmounts the previous badge node and mounts a fresh one, so the CSS animation starts from frame 0 on a brand-new element. In vanilla JS, either clone the badge node with node.cloneNode(true) and replace it, or use the same offsetWidth-reflow trick as the error-shake. The keyframe itself is a 4-stop bounce ramp (scale up, overshoot, settle) with the cart variant adding a brief rotate(-8deg) on the overshoot beat.
Production gotchas
Without the key trick, the second count change triggers no animation at all and looks broken — this is the most common badge bug. Don’t key on a random ID; key on the count value itself so equal-to-previous counts (e.g., an unread-then-read cycle landing back on the same number) also retrigger. The overshoot scale can push the badge past adjacent layout; reserve a tiny margin around the badge or anchor it with position: absolute on the parent. Avoid retriggering on every keystroke if the count is debounced upstream — the badge becomes a metronome.
Accessibility
Pair the badge with an aria-live="polite" sibling that announces the new count text (“3 unread messages”) so screen readers hear the change. The badge itself can use aria-hidden="true" since the accessible announcement lives elsewhere. Under prefers-reduced-motion: reduce drop the bounce keyframe and rely on the count-text update plus a brief color flash for the visual signal.
References
Implementation depth
A badge bounce is useful only when the count changes. Restart the animation from a state key or class toggle tied to the new value, otherwise repeated updates can be swallowed by an already-running keyframe.
The badge still needs a textual label. Color and bounce alone do not explain what changed, so expose the count in the accessible name and use reduced motion to keep the count update visible without the pop.