Pricing Toggle — Free HTML CSS JS Monthly/Annual Snippet

Pricing Toggle · Pricing · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Two price arrays (monthly/annual) swapped on toggle by iterating .price-num elements
.annual class on toggle triggers CSS translateX(28px) knob slide transition
Active label highlight: inline style.color update on toggle switch
Save X% badge per card shows on annual, hidden on monthly
Three-tier pricing cards with featured middle card
isAnnual boolean drives all UI state with one variable
Combines with Pricing Card snippet for full pricing section
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons
Live split-pane editor — preview updates as you type

About this UI Snippet

Pricing Toggle — Price Array Swap, CSS Knob Slide & Annual Discount Badge

Screenshot of the Pricing Toggle snippet rendered live

A monthly/annual billing toggle is a standard conversion pattern on SaaS pricing pages. Annual billing usually saves 20-30% — the toggle lets users see both options without a page reload. The price change combined with a "Save X%" badge nudges users toward the annual plan.

The data structure

Two arrays hold the prices: const monthly = [9, 29, 79] and const annual = [7, 23, 63]. Each index corresponds to a pricing tier. switchBilling() toggles isAnnual and selects the right array: document.querySelectorAll('.price-num') iterates all price elements and updates each textContent with the corresponding array value.

The CSS toggle knob

The toggle button gains the .annual class on switch. CSS transitions the knob: .tog-btn { transform: translateX(0) } for monthly and .tog.annual .tog-btn { transform: translateX(28px) } for annual. The background colour also changes.

Active label highlighting

switchBilling() directly sets style.color on the Monthly and Annual label elements — active label gets #f1f5f9 (bright), inactive gets #475569 (dimmed). This communicates which billing cycle is currently selected.

The save badge

An .annual child of each price shows the discount percentage (Save 22%, Save 21%, etc.). It starts hidden and gains display: flex when the toggle activates annual. The percentage values are hardcoded in the HTML — update them to match your actual discount.

The two-array price swap

Two JavaScript arrays hold prices for each plan: const monthly = [0, 12, 39] and const annual = [0, 10, 31]. When the toggle fires, it reads the current isAnnual boolean and selects the correct array. It then iterates each .amount span and updates textContent with the corresponding price. The transition: color 0.2s on .amount creates a brief colour fade as the number changes, drawing the eye to the update.

The CSS knob animation

The toggle knob uses transform: translateX(20px) in the checked state versus translateX(0) in the unchecked state. The CSS transition: transform 0.2s eases the slide. No JavaScript position calculation — the toggle is pure CSS with JavaScript only for the price swap and label active-state class toggling.

The "Save 20%" badge

The annual label contains a badge chip that is always visible, even before the toggle is switched. This is a deliberate conversion pattern — showing the savings before the user interacts means they see the benefit immediately. The badge uses a green tinted background to communicate positive value.

Connecting to Stripe

Store two Stripe price IDs per plan: one for monthly and one for annual billing. On CTA click, read the isAnnual state and pass the matching priceId to stripe.redirectToCheckout(). The toggle state determines which billing cycle the user enters at checkout.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to work out the price-swap and knob-slide interaction by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how switchBilling reads the isAnnual boolean to pick between two parallel price arrays and why each price element gets a brief opacity fade and vertical shift during the swap rather than an instant text change. The same assistant can help optimize it, for example checking whether indexing into monthly and annual arrays by a fixed [1,2,3] literal is fragile if a plan is added or reordered, or whether the featured card's styling stays legible if the accent color changes. It's also useful for extending the effect: ask it to add a third billing option (like quarterly), animate the numeric digits rolling rather than fading, or wire each tier's button to a specific Stripe price ID that depends on both the tier and the current billing period. 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 monthly/annual pricing toggle for a three-tier pricing section in plain HTML, CSS, and vanilla JavaScript — no libraries.

Requirements:
- A pill-shaped toggle switch with a circular knob that slides between two positions via a CSS transform transition, plus a "Monthly" label and an "Annual" label with an always-visible "Save X%" badge, where the inactive label dims and the active label brightens whenever the toggle state changes.
- Two parallel JavaScript arrays, one holding monthly prices and one holding annual prices, indexed identically to the three pricing tier cards, so a single boolean flip determines which array is read.
- Clicking the toggle must: flip the boolean, add or remove a CSS class on the toggle button that drives the knob's slide animation, update both labels' visual emphasis, and update each of the three displayed prices by reading from the newly active array.
- Each displayed price number must play a brief transition when it changes — fade its opacity down and shift it slightly, swap the text content, then fade and shift it back — rather than the number changing instantly with no visual feedback.
- One of the three tier cards must be visually marked as the featured/recommended plan with a distinct accent border, a background gradient or glow, and a floating "Most popular" badge centered above its top edge.
- Every feature list item in each card must show a checkmark or a cross via CSS pseudo-elements (not typed characters), with excluded features additionally dimmed in color.

Step by step

How to Use

  1. 1
    Click the toggleClick the Monthly/Annual toggle to switch between price arrays. The knob slides, labels dim/brighten, and the Save badges appear.
  2. 2
    Update pricesIn the JS panel, update the monthly and annual arrays to your actual prices. Each index corresponds to a pricing tier card.
  3. 3
    Update save percentagesIn the HTML panel, update the Save X% text in each .annual badge to match your actual annual discount.
  4. 4
    Update plan names and featuresIn the HTML panel, update the tier names (Starter, Pro, Team), the price labels, and the feature lists.
  5. 5
    Change the accent colourFind #6366f1 in the CSS and replace with your brand colour. Updates the toggle background, featured card, and save badge.
  6. 6
    Export 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

SaaS pricing page billing toggle
The definitive SaaS conversion pattern. Annual billing with a save badge visible immediately after toggle increases annual plan selection rate.
Subscription product tier comparison
Show monthly and annual prices side-by-side for three tiers. The toggle lets users compare total annual spend vs monthly flexibility.
Learn toggle state management
Edit the monthly and annual arrays and the switchBilling function in the JS panel to understand how one boolean drives all UI state changes.
Prototype pricing UX
Use the snippet to test which annual discount percentage drives the most annual plan selections in a prototype before building a real pricing page.
Combine with Pricing Card snippet
Use this toggle with the Pricing Card snippet for a complete pricing section, or above a comparison table for a feature matrix. The toggle drives the price display; the cards provide the plan comparison layout.
Extend to a 4th plan or currency
Add a fourth value to both arrays for an Enterprise tier. Or add a currency selector that multiplies all prices by a rate — one variable drives all prices.

Got questions?

Frequently Asked Questions

Two arrays hold the prices for each tier. switchBilling() picks the active array (isAnnual ? annual : monthly) and iterates document.querySelectorAll(".price-num") to set each element's textContent to the corresponding array value.

The .tog-btn has CSS transition: transform 0.2s. In the default state, transform: translateX(0). When .annual is added to .tog, the CSS rule .tog.annual .tog-btn { transform: translateX(28px) } slides it right.

Add a fourth value to both monthly and annual arrays. Add a fourth .card div to the HTML with matching structure. The querySelectorAll iteration picks it up automatically by index.

Store a currentRate variable. In switchBilling(), multiply each price by currentRate before displaying. Add a currency dropdown that updates currentRate and calls switchBilling() again to refresh displayed prices.

Add data-price-id attributes to each CTA button. In the button onclick, read the active billing cycle and the tier price ID, then call stripe.redirectToCheckout({ priceId }) with the appropriate ID from your Stripe dashboard.

Yes. Click "JSX" for a React component. Manage isAnnual in useState. Derive displayed prices as isAnnual ? annual[i] : monthly[i] in the render. The CSS toggle animations work unchanged.