Counter Badge

A vertical HUD-style counter for lesson chrome. Three stacked rows — a monospace label on top, a large tabular-nums value in the middle, and a proportional progress bar underneath. When the value reaches maxValue, the number and the fill bar swap to the success tone and the digit gains a soft glow halo; the optional isNewMax flag plays a brighter pop the next time the value changes.

Reach for it when a lesson canvas needs a corner-of-screen counter — sweep-line overlap, DFS stack depth, window element count, frequency-counter peaks — that should feel like a HUD readout, not body copy.

Active
0
Customize
Counter
8
700
Style
default

Installation

npx shadcn@latest add https://craftbits.dev/r/counter-badge.json

Usage

import { CounterBadge } from "@craft-bits/edu";
 
<CounterBadge value={active} maxValue={peak} label="Active" />

Mark the moment the parent records a fresh peak so the next value change pulses the brighter celebration beat:

<CounterBadge
  value={active}
  maxValue={peak}
  label="Active"
  isNewMax
/>

Drop the progress bar entirely when the counter is part of a denser stat row:

<CounterBadge value={depth} maxValue={maxDepth} label="Depth" hideTrack />

Anatomy

  • Rootinline-flex flex-col items-end, spreads unknown props onto a <div>.
  • Label — small monospace, muted, uppercase, tracking-wider. Pass null or empty string to suppress.
  • Value — large tabular-nums, coloured by tone while climbing and by cb-success at peak. Keyed by value so every update re-runs a small scale beat on SPRINGS.bouncy.
  • Peak glow — a soft text-shadow halo using color-mix(in srgb, var(--cb-success), transparent) so the glow adapts to light/dark themes without a hardcoded hex.
  • Progress bar — animates scaleX (origin-left) instead of width so the motion is GPU-composited. Hidden by hideTrack.
  • New-max beat — the badge tracks the previous peak state in a ref; the brighter 1.05 scale beat fires once on the transition into peak, not on every render after.
  • Reduced motionprefers-reduced-motion: reduce short-circuits the entrance scale and the glow halo.

Props

PropTypeDefaultDescription
valuenumberrequiredCurrent counter value. Drives the pop-on-change animation and the progress fill.
maxValuenumberrequiredPeak value the counter is climbing toward. Triggers the success glow when value >= maxValue.
labelReactNode'Active'Small monospace label above the number. Pass null or empty string to suppress.
tone'default' | 'accent' | 'info' | 'warning' | 'error''default'Resting tone — at peak the badge always swaps to success.
isNewMaxbooleanfalsePlays a brighter celebratory pop on the next value change.
hideTrackbooleanfalseDrop the progress bar; keep the label + value.
classNamestringMerged onto the root via cn().

Accessibility

  • The value carries aria-live="polite" and aria-atomic="true" so screen readers announce the new number on every change without interrupting.
  • The progress track is aria-hidden — it is a decorative reflection of the value / maxValue ratio, not an independent fact.
  • Motion is transform / opacity / filter / text-shadow only — never width / height / top / left. The fill bar animates scaleX, not width.
  • prefers-reduced-motion: reduce short-circuits the entrance scale beat and the peak glow halo — the new value appears instantly at scale 1 with no shadow.
  • The peak glow uses color-mix(in srgb, var(--cb-success), transparent) rather than a pure-black shadow, so it stays inside the design-token palette and adapts to dark mode.

Credits

  • Extracted from: algoflashcards (src/lessons/primitives/chrome/CounterBadge.tsx). The source paired hardcoded rgba(34, 197, 94, 0.30) glow with project-specific SEMANTIC_HEX['success'], a width animation on the progress fill, and accentHex / accentGlow raw-string overrides. craft-bits' version retones to cb-* semantic tokens, swaps the width animation for a GPU-composited scaleX transform, replaces the raw-string overrides with a tone enum, and adds an explicit "new max" celebratory beat that fires only on the transition into peak.