Loyalty Points Widget — Rewards HTML CSS JS Snippet

Loyalty Points Widget · Cards · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Single-source points model
One points variable and updateUI drive the balance, progress bar, tier copy, and every reward button — trivial to wire to a real account.
Animated tier progress bar
updateUI sets the gradient bar's width to points / NEXT_TIER with a cubic-bezier transition and a glow, capping at 100%.
Affordability-gated buttons
Each reward enables only when affordable; otherwise it shows "Need N", turning a disabled state into a concrete goal.
Eased balance count-down
animateBalance tweens the number with a cubic ease-out requestAnimationFrame loop, making each spend feel tangible.
Automatic re-gating
After a redemption updateUI re-evaluates every reward against the new balance, flipping now-unaffordable buttons to "Need N".
Auto-dismissing toast
A confirmation pill slides up and clears its own timer on each show, so rapid redemptions never stack stale messages.
Data-attribute reward costs
Reward prices live on data-cost, so adding or repricing rewards is a markup edit with no JavaScript changes.
Premium dark styling
A gradient card with a radial glow, glowing tier dot, and gold accents gives membership the elevated look loyalty programmes rely on.

About this UI Snippet

Loyalty Points Widget — Tier Progress Bar, Redeemable Rewards & Affordability-Gated Buttons

Screenshot of the Loyalty Points Widget snippet rendered live

Loyalty programmes work because they give customers a reason to come back and a visible sense of progress toward a reward. The widget that surfaces a member's points has to do three jobs at once: show the current balance with status, show how close they are to the next tier, and let them spend points on rewards they can actually afford. This snippet implements all three in plain HTML, CSS, and vanilla JavaScript: a points balance with tier badge, an animated progress bar toward the next tier, a list of redeemable rewards, affordability-gated redeem buttons, an animated count-down on spend, and a confirmation toast.

Balance, tier, and progress

The card leads with a large points balance and a "Gold member" tier badge on a dark gradient with a warm glow — the premium treatment that loyalty programmes use to make membership feel valuable. Below it, updateUI computes progress toward NEXT_TIER as a percentage, animates the gradient bar's width with a cubic-bezier transition, and writes the remaining points ("320 pts to Platinum"). When the balance reaches the threshold, the copy switches to "Platinum unlocked!".

Affordability-gated redemption

Each reward carries its cost on a data-cost attribute. updateUI walks every reward and decides its button state: if the member can afford it, the button reads "Redeem"; if not, it disables and reads "Need N" — telling the user exactly how many more points they require rather than just greying out silently. This turns a dead-end into a goal, which is the whole point of a loyalty programme.

Animated balance count-down

When a reward is redeemed, redeem checks affordability, subtracts the cost, and calls animateBalance, which tweens the displayed number from the old value to the new one over 500ms using an eased requestAnimationFrame loop (cubic ease-out). Watching the points tick down makes the spend feel tangible — and then updateUI re-runs to re-gate every other reward against the new, lower balance, so buttons that are now unaffordable immediately switch to "Need N".

Confirmation toast

A pill toast slides up from the bottom of the card confirming the redeemed reward by name, auto-dismissing after a moment via a cleared-and-reset timer so rapid redemptions never stack stale toasts. The redeemed reward's own button locks to "✓ Done".

Everything is driven by a single points variable and the updateUI function, so wiring it to a real account is a matter of seeding points from your API and POSTing redemptions. Pair this widget with a stats card for account metrics, a progress bar for other goals, a gradient progress ring, or an order summary where vouchers apply.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You do not need to trace the affordability-gating logic in your head. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how updateUI() re-evaluates every reward's data-cost attribute against the live points variable to decide between a "Redeem" and a "Need N" label, and why animateBalance() uses a cubic ease-out inside requestAnimationFrame rather than just snapping the number to its new value. The same assistant can help optimize it, for instance asking whether rebuilding every reward button's disabled state and text on every single redemption is necessary or whether only the affected rewards need re-checking. It is also useful for extending the widget: ask it to add multiple tiers with different perks, persist redemptions to a real backend with optimistic rollback on failure, or add a reduced-motion fallback for the balance count-down animation. 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:

text
Build a "loyalty points widget" in plain HTML, CSS, and JavaScript with no libraries.

Requirements:
- A card showing a large points balance, a membership tier label, and a progress bar toward the next tier threshold, where the bar's fill width and the "N points to next tier" copy are both derived from one shared points variable and a next-tier constant.
- A list of redeemable rewards, each carrying its point cost as a data attribute on its row, with a redeem button per reward.
- A single update function that walks every reward row and sets its button's disabled state and label purely from comparing the live points balance to that reward's cost: enabled and labeled "Redeem" when affordable, disabled and labeled with exactly how many more points are needed (e.g. "Need 340") when not.
- Redeeming a reward must: verify affordability again at click time (not just trust the disabled state), subtract its cost from the points variable, animate the displayed balance number from its old value to its new value over a fixed short duration using an eased requestAnimationFrame tween (not an instant text swap), lock that specific reward's button to a permanent "done" state so it cannot be redeemed twice, and re-run the shared update function afterward so every other reward's affordability label refreshes against the new lower balance.
- Show a temporary confirmation toast naming the redeemed reward, auto-dismissing after a couple of seconds, with any pending dismiss timer cleared and restarted on each new redemption so rapid redemptions never leave stale toasts stacked or flickering.
- The tier progress bar's fill percentage must be capped at 100 even if the points balance exceeds the next-tier threshold, and the copy must switch to an "unlocked" message once the threshold is reached.

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

  1. 1
    Paste HTML, CSS, and JSA dark loyalty card appears showing 1680 points, a "Gold member" badge, a progress bar at 84% toward Platinum, and four rewards.
  2. 2
    Check affordability gatingRewards you can afford show "Redeem"; the 2500-point members-only drop is disabled and reads "Need 820", telling you exactly how short you are.
  3. 3
    Redeem a rewardClick "Redeem" on the $5 voucher — the balance counts down from 1680 to 1180, a toast confirms "Redeemed $5 voucher!", and the button locks to "✓ Done".
  4. 4
    Watch the bar updateThe progress bar shrinks toward the next tier and the "pts to Platinum" figure recalculates from the new balance.
  5. 5
    See re-gating in actionAfter spending, rewards you can no longer afford flip to "Need N" automatically — the whole list re-evaluates against the new balance.
  6. 6
    Seed real dataSet the points variable from your account API and adjust NEXT_TIER and reward data-cost values; the rest of the UI follows.

Real-world uses

Common Use Cases

E-commerce rewards programmes
Show points and let shoppers redeem vouchers and perks. Apply redeemed vouchers in an order summary at checkout.
Account and profile dashboards
Embed it in a member dashboard beside a stats card for spend, orders, and tier history.
Cafe and retail apps
A digital stamp/points card with redeemable freebies — the tier progress maps to the next free coffee or discount.
Gamified product engagement
Reward in-app actions with points toward perks; pair the progress with a progress bar for other milestones.
Referral and credit balances
Show referral credits and let users spend them, with affordability gating preventing overspend of available credit.
Subscription perks and tiers
Surface tier benefits and a path to the next level, alongside a subscription widget for plan management.

Got questions?

Frequently Asked Questions

Seed the points variable from your loyalty API on load and render the reward rows from your catalogue, setting each data-cost. Call updateUI() once and the progress bar, tier copy, and button gating all reflect the live balance. Set NEXT_TIER from the member's current tier threshold.

In redeem, after the affordability check, POST the reward id to your endpoint and only commit the local deduction on success; on failure, revert the balance and re-enable the button. To avoid double-spends from rapid clicks, disable the button immediately (this snippet does) and treat the server's returned balance as authoritative.

Keep an array of tiers with thresholds and names, and in updateUI find the current and next tier from points to drive the badge and the "pts to next" copy. You can also list the perks unlocked at each tier below the bar so members see what the next level grants.

Give the progress bar role="progressbar" with aria-valuenow/aria-valuemax, put the redeem confirmation toast in an aria-live="polite" region, and make sure the "Need N" button text conveys the gating without relying on colour. The redeem controls are real <button> elements, so they are keyboard-operable by default.

In React, hold points in useState, derive the percentage and each button's disabled/label state in render, and animate the balance with a small effect or a tween library. In Vue, use a ref for points and computed values for progress and gating. In Angular, track points on the component and bind [style.width] and [disabled]. The card styling and bar CSS port unchanged.