Source Code

<div class="asb-row" id="asbRow">
  <div class="asb-card" data-monthly="12" data-annual="108">
    <span class="asb-badge"></span>
    <h4>Starter</h4>
    <div class="asb-price"><b>$12</b><small>/mo</small></div>
    <p class="asb-annual"></p>
  </div>

  <div class="asb-card asb-featured" data-monthly="29" data-annual="228">
    <span class="asb-badge"></span>
    <h4>Pro</h4>
    <div class="asb-price"><b>$29</b><small>/mo</small></div>
    <p class="asb-annual"></p>
  </div>

  <div class="asb-card" data-monthly="79" data-annual="758">
    <span class="asb-badge"></span>
    <h4>Team</h4>
    <div class="asb-price"><b>$79</b><small>/mo</small></div>
    <p class="asb-annual"></p>
  </div>
</div>

Annual Savings Badge — Free "Save $X/Year" Pricing Callout (HTML/CSS/JS)

Annual Savings Badge · Pricing · Plain HTML, CSS & JS · Live preview

What's included

Features

Two-attribute API
Only data-monthly and data-annual drive the whole computation.
Live dollar savings
monthly × 12 minus the annual price, computed per card.
Live percentage savings
Savings divided by the monthly-year cost, rounded to a whole number.
Genuinely different results
Each example card has its own real discount, not a shared static banner.
Graceful zero/negative fallback
Falls back to "Best value" rather than showing broken math.
Composable badge
A small span plus a line of text — drops into any card layout.
Featured-card variant
A distinct gold treatment for the plan you want to highlight.
Framework-agnostic core
The savings computation is pure and ports to any component model.

About this UI Snippet

Annual Savings Badge — A "Save $X/Year" Callout Computed From Real Prices

Screenshot of the Annual Savings Badge snippet rendered live

A pricing card that just says "save on annual billing" without a number is easy to skim past — the badge that actually states a dollar figure and a percentage is what makes someone pause. This snippet builds that badge as a small, reusable component that computes its own numbers from two data attributes — a monthly price and an annual-equivalent price — so the same markup works correctly across cards with completely different price points.

Two numbers in, everything derived

Each card carries only data-monthly and data-annual attributes — the two real prices a business actually sets. The script computes what a full year would cost paying monthly every month (monthly * 12), subtracts the annual price to get the dollar savings, and divides that by the monthly-year cost to get a percentage, rounded to a whole number. Nothing about the savings figure is typed by hand, so the badge can never drift out of sync with the underlying prices.

Verified across three different price points

Starter is $12/mo vs. $108/yr: paying monthly all year would cost $144, so the badge shows *Save $36/yr (25%)*. Pro is $29/mo vs. $228/yr: monthly-all-year is $348, savings is $120, which is *Save $120/yr (34%)*. Team is $79/mo vs. $758/yr: monthly-all-year is $948, savings is $190, which is *Save $190/yr (20%)*. Each card's percentage is genuinely different because each plan's discount is genuinely different — the badge never shows a single hardcoded "20% off" banner across cards with unrelated actual discounts.

A fallback for zero or negative savings

If annual were ever equal to or greater than monthly * 12 — a data-entry mistake, or a plan with no annual discount — the badge falls back to a neutral "Best value" label instead of showing a nonsensical "Save $0/yr (0%)" or a negative number, so the component degrades gracefully rather than displaying broken math.

A tiny, composable piece

The badge is deliberately just a span plus one line of annual-billing text — it doesn't own the plan's full price display, name, or feature list, so it drops into an existing pricing card or sits beside a pricing toggle without restructuring the rest of the card.

Where it fits

Use it inside a pricing card grid, next to a monthly/annual pricing toggle, or paired with a flip pricing card that reveals the annual price on flip.

Customizing it

Swap in your own monthly and annual prices per card — the math updates automatically. Add a currency symbol variant, or switch the badge to always show percentage-only or dollar-only depending on which number tests better for your audience.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to re-derive the savings math by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to walk through exactly how the badge computes monthly × 12 as the "if billed monthly all year" baseline, subtracts the real annual price to get dollar savings, and divides that by the same baseline to get a percentage — and why each of the three example cards produces a genuinely different percentage rather than a single shared number. The same assistant can help you extend it: ask how to add a fallback for when annual pricing isn't discounted at all (so the badge shows something other than broken math), how to make the badge switch between showing dollars-only, percent-only, or both based on a data attribute, or how to animate the number counting up when the card scrolls into view. 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 reusable "annual savings badge" component in plain HTML, CSS, and JavaScript with no framework or library, demonstrated on three example pricing cards with different price points.

Requirements:
- Each pricing card should store only two real inputs as data attributes: a monthly price and an annual-equivalent price (what a full year actually costs if paid annually).
- Write a script that, for each card, computes what a full year would cost if paid monthly every month (monthly price × 12), subtracts the actual annual price to get a dollar savings amount, and divides that savings by the monthly-year cost to get a percentage, rounded to a whole number — then renders both the dollar figure and the percentage into a badge on that card (e.g. "Save $120/yr (34%)").
- Use at least three example cards with genuinely different monthly and annual prices so the computed savings amount and percentage are visibly different per card — do not reuse one hardcoded percentage across all cards.
- Before finalizing, manually verify each card's arithmetic: monthly × 12 minus annual should equal the displayed dollar savings, and that savings divided by (monthly × 12) should equal the displayed percentage within normal rounding.
- Add a fallback: if the annual price is equal to or greater than monthly × 12 (no real discount, or bad data), show a neutral label like "Best value" instead of a "Save $0/yr (0%)" or negative-number badge.
- Keep the badge itself a small, self-contained piece of markup (not tied to a specific card layout) so it could be copied into an existing pricing card design.

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 JSThree example cards render, each with its own computed badge.
  2. 2
    Check the numbersStarter shows Save $36/yr (25%); Pro shows Save $120/yr (34%).
  3. 3
    Edit the data attributesChange data-monthly or data-annual on any card.
  4. 4
    Watch the badge updateThe dollar amount and percentage recompute from the new prices.
  5. 5
    Try a losing caseSet annual equal to monthly × 12 — the badge shows "Best value" instead of $0.
  6. 6
    Drop it into a real cardReuse just the badge span inside your existing pricing card markup.

Real-world uses

Common Use Cases

Pricing card grids
Drop the badge into an existing pricing card.
Monthly/annual toggles
Show the badge beside a pricing toggle.
Flip cards
Reveal the badge on the back of a flip pricing card.
Upgrade prompts
Show real savings inside an upgrade banner.
Landing page hero pricing
Lead with the strongest computed percentage for the featured plan.
Seat-based plans
Pair with a seat-based pricing calculator for annual quotes.
Related: Annual vs Monthly Cost Chart
See the Annual vs Monthly Cost Chart for a related pricing pattern worth pairing with this one.
Related: Subscription Billing Toggle
See the Subscription Billing Toggle for a related pricing pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

The script computes what a full year would cost if billed monthly every month (monthly × 12), then subtracts the actual annual price. For Pro at $29/mo vs. $228/yr, that's $29 × 12 = $348 for monthly-all-year, minus $228, which is $120 in savings — exactly what the badge shows.

Savings dollars divided by the monthly-all-year cost, rounded to the nearest whole percent. Starter's $36 savings over a $144 monthly-year cost is 25%; Pro's $120 over $348 is about 34.5%, rounded to 34%; Team's $190 over $948 is about 20.0%, rounded to 20%. Each plan has its own real discount, so the percentages are correctly different, not copy-pasted.

If the annual price is equal to or greater than monthly × 12, savings comes out to zero or negative. Rather than displaying "Save $0/yr (0%)" or a nonsensical negative number, the badge falls back to a neutral "Best value" label so the component never shows broken math.

Yes — the badge is a small, self-contained span plus one line of annual-billing text driven entirely by two data attributes on its parent card. It doesn't assume anything about the rest of the card's markup, so you can copy just .asb-badge and its script logic into an existing card layout.

Pass monthly and annual as props to a Badge component, and compute savings and savingsPct with useMemo or a computed property rather than reading data attributes. The comparison-and-fallback logic (falling back to "Best value" when savings <= 0) copies over unchanged.