Pricing Cards with Live Plan Popularity Counter — Free HTML CSS JS Snippet
Pricing Cards with Live Plan Popularity Counter · Pricing · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Pricing Cards with Live Popularity Counter — Independent Per-Card Activity Ticks

A "Most popular" badge on one card is a claim; a number that visibly moves is closer to evidence. This pricing grid gives every plan its own small live counter — "N people picked this in the last hour" — that ticks upward on its own independent, randomized rhythm, so the activity feels like real concurrent signups across three plans rather than one obviously scripted global timer.
Every card runs its own independent timer, deliberately unsynchronized
scheduleNextBump(el) is called once per counter and re-schedules itself recursively after each bump, with a randomized delay between 4 and 11 seconds (4000 + Math.random() * 7000) picked fresh every time. Because each of the three counters calls this independently, they never bump in lockstep — which is exactly what real, unrelated signups across three different plans would look like. A single shared setInterval ticking all three counters at once would have been simpler to write, but would visibly read as fake the moment two cards moved in perfect sync.
The base number is real content, not a placeholder
Each .ppc-pop-count starts from a data-base value baked into the HTML — 6, 41, and 14 for Starter, Growth, and Scale respectively — reflecting a plausible real distribution where the featured "Growth" plan gets picked far more often. The JavaScript only ever increments from whatever textContent currently reads via parseInt, so the starting numbers are genuinely the page's real content, not zero-state placeholders waiting for a script to fill in.
Mostly +1, occasionally +2, never a round jump
bump() increments by 1 seventy percent of the time and by 2 the other thirty percent (Math.random() < 0.7 ? 1 : 2), avoiding both a suspiciously mechanical "always +1 on a fixed timer" pattern and an unrealistic "sometimes jumps by 10" pattern that would draw attention to itself as obviously synthetic.
A brief color flash marks the moment of change
When a counter bumps, .ppc-pop-bump applies a green color for 600ms before being removed — a visitor who happens to be looking at a card when its number ticks gets a clear, brief acknowledgment that something just changed, without a distracting animation that would fire indefinitely.
The pulsing dot is a constant CSS animation, unrelated to the tick timing
The small dot beside each counter uses a CSS @keyframes pulse running continuously and independently of the JavaScript-driven number ticks — it signals "this is live" as ambient, constant motion, while the actual number change is the more meaningful (and rarer) event that draws a second glance.
Customizing it
Adjust each card's data-base value to reflect real signup numbers from your own analytics rather than fabricated ones — the honesty of this pattern depends entirely on starting from a real distribution. Change the 4000–11000 millisecond delay range in scheduleNextBump() to slow down or speed up the perceived tick rate; a much shorter range reads as busier but can feel artificial if overdone.
Build with AI
Build, Understand, Optimize, and Extend It With AI
Instead of guessing why the counters never seem to move together, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why each counter schedules its own independently randomized timer via scheduleNextBump() rather than sharing one setInterval across all three cards, and why the increment logic mixes +1 and +2 instead of always incrementing by a fixed amount. The same assistant can help you extend it — ask it to replace the simulated random bumps with real periodic fetch calls to a backend endpoint that returns actual signup counts per plan, add a subtle "N people are viewing this plan right now" secondary signal using the same staggered-timer technique, or rate-limit how frequently the color flash can retrigger if a counter happens to bump twice in quick succession. It's also useful for an honesty review: ask whether simulated activity counters should be replaced with real data before shipping to production, since fabricated urgency signals can undermine trust once a visitor notices the pattern. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a three-tier pricing card grid in plain HTML, CSS, and vanilla JavaScript where each card shows its own live-ticking "N people picked this in the last hour" counter — no WebSocket, no backend, no animation library.
Requirements:
- Three pricing cards (e.g. Starter, Growth, Scale), the middle one visually featured as "Most popular," each with a plan name, price, a short feature checklist, a call-to-action button, and a small popularity line containing a pulsing activity dot and a bold number.
- Each counter must start from its own real starting number (set directly in the HTML, not zero), reflecting a plausible distribution where the featured plan already has a noticeably higher count than the others.
- Each counter must independently schedule its own next increment using a randomized delay (e.g. between 4 and 11 seconds), re-scheduling itself after every tick — the three counters must NOT be driven by one shared interval that would make them all tick at the same synchronized moment.
- When a counter increments, it should mostly go up by 1 but occasionally jump by 2 (roughly 70/30), and briefly flash a distinct color for under a second to acknowledge the change before returning to its normal color.
- The small activity dot next to each counter should use a continuous CSS pulse animation that runs independently of the JavaScript-driven number increments — it should never stop or restart in response to a counter tick.
- Read each counter's current value directly from its displayed text content when incrementing (do not track the count in a separate JavaScript variable disconnected from what is shown).Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1Watch each card's counterNumbers tick up independently on staggered random timers — they will not move in sync.
- 2Notice the brief color flashEach counter flashes green for a moment right when it increments.
- 3Set real starting numbersEdit the data-base attribute and initial text on each .ppc-pop-count element to reflect your actual signup data.
- 4Adjust the tick frequencyChange the 4000 and 7000 millisecond values inside scheduleNextBump() in the JS panel.
- 5Change the increment patternEdit the 0.7 probability and the 1/2 increment values inside bump() in the JS panel.
- 6Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
No, deliberately not. Each counter calls its own scheduleNextBump() with a freshly randomized delay between 4 and 11 seconds every time it fires, so the three cards bump at unrelated moments — which is what real, independent signups across three different plans would actually look like, rather than one obviously synchronized global timer.
They are meant to be real: each .ppc-pop-count element starts with an actual number in its text content and a matching data-base attribute (6, 41, and 14 in this example). The JavaScript only increments from whatever the element currently displays via parseInt — it never resets or overwrites the base number you set, so replacing those starting values with your own real signup data is the entire integration step.
bump() increments by 1 about 70% of the time and by 2 the other 30%, controlled by Math.random() < 0.7. A counter that always incremented by exactly 1 on a fixed rhythm would start to look mechanical and scripted after a few ticks; the small amount of variation reads as more like genuinely independent real-world events.
When bump() runs, it adds a ppc-pop-bump class that turns the number green, then removes it after 600 milliseconds via setTimeout. It is a lightweight visual acknowledgment for a visitor who happens to be looking at the number right when it changes, without a persistent or distracting animation.
No — the dot uses a continuous CSS @keyframes animation that runs independently on a fixed loop, unrelated to the JavaScript-driven number ticks. It signals general "this is live" activity as constant ambient motion, while the counter number itself is the more meaningful, comparatively rare event.
No, it is a fully client-side simulation using setTimeout and Math.random() — no server connection is required. For a genuinely real-time count reflecting actual signups, you would replace the simulated bump() logic with periodic fetch calls to a real backend endpoint that returns current counts.