Source Code

<section class="hpc-hero">
  <div class="hpc-copy">
    <span class="hpc-eyebrow">Build your plan</span>
    <h1 class="hpc-h1">Configure it your way,<br>see the price update live</h1>
    <p class="hpc-sub">Pick a tier, choose your seats, and toggle add-ons — the preview card and price recalculate instantly as you go.</p>
  </div>

  <div class="hpc-panel">
    <div class="hpc-group">
      <span class="hpc-group-label">Plan</span>
      <div class="hpc-pills" id="hpcTierPills">
        <button type="button" class="hpc-pill active" data-tier="starter" data-price="9">Starter</button>
        <button type="button" class="hpc-pill" data-tier="growth" data-price="29">Growth</button>
        <button type="button" class="hpc-pill" data-tier="scale" data-price="79">Scale</button>
      </div>
    </div>

    <div class="hpc-group">
      <span class="hpc-group-label">Seats: <b id="hpcSeatsVal">5</b></span>
      <input type="range" id="hpcSeats" class="hpc-slider" min="1" max="50" step="1" value="5">
    </div>

    <div class="hpc-group">
      <span class="hpc-group-label">Add-ons</span>
      <div class="hpc-addons" id="hpcAddons">
        <label class="hpc-addon"><input type="checkbox" data-price="15" data-name="Advanced analytics"><span>Advanced analytics</span><b>+$15/mo</b></label>
        <label class="hpc-addon"><input type="checkbox" data-price="10" data-name="Priority support"><span>Priority support</span><b>+$10/mo</b></label>
        <label class="hpc-addon"><input type="checkbox" data-price="25" data-name="Custom domain"><span>Custom domain</span><b>+$25/mo</b></label>
      </div>
    </div>

    <div class="hpc-preview" id="hpcPreview">
      <div class="hpc-preview-top">
        <span class="hpc-preview-tier" id="hpcPreviewTier">Starter</span>
        <span class="hpc-preview-seats" id="hpcPreviewSeats">5 seats</span>
      </div>
      <ul class="hpc-preview-addons" id="hpcPreviewAddons"></ul>
      <div class="hpc-price-row">
        <span class="hpc-price" id="hpcTotal">$54</span>
        <span class="hpc-price-sub">/month, billed monthly</span>
      </div>
      <a href="#" class="hpc-cta">Start with this plan</a>
    </div>
  </div>
</section>

Hero with Interactive Product Configurator — Free HTML CSS JS Snippet

Hero with Interactive Product Configurator · Heroes · Plain HTML, CSS & JS · Live preview

What's included

Features

Single recalc() function reads all inputs fresh from the DOM on every change
Per-seat pricing model — tier price multiplies by the seats slider value
Add-on checkboxes rebuild the preview card's itemized list, not just the total
Price bump micro-animation retriggers correctly via a forced reflow
Tier pills use a simple active-class pattern, easy to extend with more tiers
No JS-side price object to keep in sync — DOM state is the single source of truth
Responsive layout collapses cleanly to a single column on mobile
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

Hero Section with Product Configurator — Live Plan, Seats & Add-On Pricing

Screenshot of the Hero with Interactive Product Configurator snippet rendered live

Most pricing heroes just link out to a pricing page. This one lets a visitor actually configure a plan right there above the fold — pick a tier, drag a seats slider, toggle add-ons — and watch a preview card and total price recompute on every change, with zero page navigation and zero guessing about what the final number will look like.

One `recalc()` function, three independent inputs

The tier pills, the seats <input type="range">, and the add-on checkboxes are three completely different input types, but they all funnel into a single recalc() call. That function reads the currently active tier's data-price, multiplies it by the seat count, sums the data-price of every checked add-on, and writes the combined total into #hpcTotal. Because every input handler calls the same function rather than each maintaining its own partial update, there's no risk of the seats slider updating the price while a tier change leaves it stale — the whole total is always recomputed from scratch off the current DOM state.

Per-seat pricing, not flat pricing

The base cost isn't just the tier's price — it's tierPrice * seats, so dragging the seats slider changes the total proportionally to the selected tier. This mirrors how most real per-seat SaaS pricing works, and it's why the preview updates noticeably when you go from 5 seats to 30 rather than staying flat.

A preview card that lists exactly what you picked

previewAddons.innerHTML is rebuilt from an array assembled during the same recalc() pass — only the add-ons currently checked are listed, each showing its own price. Nothing is toggled with CSS visibility; the list content itself is regenerated every time, so removing an add-on removes its line entirely rather than hiding it.

The price "bump" micro-interaction

After updating the number, the code removes and immediately re-adds a .bump class on #hpcTotal, forcing a reflow (void totalEl.offsetWidth) in between so the CSS transform: scale transition retriggers even if the class was already present from the previous change — without the forced reflow, adding the same class twice in a row would be a no-op and the animation wouldn't replay.

Reading state straight from the DOM

Rather than keeping a separate JavaScript object in sync with every checkbox and slider, recalc() reads selectedTierPill(), seatsInput.value, and each add-on's .checked state directly at calculation time. This keeps the DOM as the single source of truth and avoids the class of bugs where a JS-side price object drifts out of sync with what's actually rendered.

Customizing it

Add more tiers by adding another .hpc-pill with its own data-tier/data-price, or more add-ons by adding another .hpc-addon checkbox with data-price/data-name. Change the per-seat multiplier logic in recalc() if your pricing model is flat-rate instead of per-seat, or add a currency formatter if you need commas or a non-USD symbol. Pair it with a full pricing add-on selector further down the page for visitors who scroll past the hero wanting more detail.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Rather than puzzling through how three different input types stay in sync, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why recalc() re-reads every input's current state from the DOM on each call instead of maintaining a separate JavaScript state object, and what class of bug that pattern avoids. The same assistant can help you extend it — ask it to add a monthly/annual billing toggle that multiplies the total and shows a savings badge, persist the visitor's configuration to the URL as query parameters so a shared link reopens with the same selections, or animate the seats slider's thumb with a live tooltip showing the current seat count as it's dragged. It's also useful for hardening the approach: ask whether the pricing data should move into a single JS array (rather than living in data attributes) once the number of tiers and add-ons grows large, or how to add currency formatting for non-USD locales. 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 hero section in plain HTML, CSS, and vanilla JavaScript containing a live, interactive product/plan configurator — no library, no framework.

Requirements:
- A hero headline and subheading above a configurator panel containing: a row of selectable "tier" pill buttons each with its own price, a range slider for number of seats with a live label showing the current value, and a list of add-on checkboxes each with its own price and name.
- A preview card that shows the currently selected tier's name, the current seat count, an itemized list of only the currently checked add-ons (with their prices), and a single total price.
- The total price must be calculated as the selected tier's price multiplied by the number of seats, plus the sum of every checked add-on's price — and it must recompute correctly no matter which control (tier, slider, or checkbox) was just changed, by reading all three inputs' current state directly rather than maintaining three separately-updated partial totals.
- Every change to any control (clicking a tier pill, dragging the slider, checking or unchecking an add-on) must immediately re-render both the preview card's add-on list and the total price — no submit button, no page reload.
- Add a small "bump" scale animation on the total price element that replays on every recalculation, including consecutive recalculations that would otherwise apply the same CSS class twice in a row (explain in a comment how forcing a reflow makes the animation retrigger correctly in that case).
- Keep the whole thing responsive down to a narrow mobile viewport.

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
    Click a tier pillStarter, Growth, or Scale — the preview card and price update immediately.
  2. 2
    Drag the seats sliderThe seat count label and the total price recalculate on every drag movement, not just on release.
  3. 3
    Toggle an add-on checkboxThe preview card's add-on list and the total both update to reflect exactly what's checked.
  4. 4
    Watch the price bumpEvery recalculation retriggers a small scale animation on the total to draw the eye to the change.
  5. 5
    Add your own tiers or add-onsCopy a .hpc-pill or .hpc-addon element and set its own data-price attribute.
  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 landing pages with usage-based pricing
Let visitors see a realistic price for their own team size before ever visiting the pricing page.
Sales-assisted enterprise tools
Give prospects a rough self-serve estimate above the fold, then route the exact configuration into a custom quote form.
Product-led growth homepages
Reduce the number of clicks between landing and understanding cost — the configurator answers "how much would this cost me" instantly.
Learn DOM-driven state patterns
Study how recalc() treats the DOM itself as the source of truth instead of duplicating input state into a JS object.
Internal tools and quoting dashboards
Reuse the same pattern for an internal sales tool that estimates a customer's monthly bill from a handful of toggles.
Related: Pricing Add-On Selector
Pair with the Pricing Add-On Selector for a dedicated below-the-fold version of the same recalculating pattern.

Got questions?

Frequently Asked Questions

Every input — the tier pills, the seats range slider, and the add-on checkboxes — calls the same recalc() function on change. That function reads the currently selected tier's data-price, the slider's current value, and every checkbox's checked state fresh from the DOM each time, then recomputes the whole total from scratch rather than trying to apply a partial update.

Per-seat. The base cost is tierPrice multiplied by the number of seats selected on the slider, so increasing the seat count scales the total proportionally to whichever tier is active, matching how most real per-seat SaaS pricing works.

previewAddons.innerHTML is regenerated from an array built during the same recalc() pass containing only the currently checked add-ons. Rebuilding the list (rather than toggling visibility on pre-rendered items) guarantees the preview never shows a stale add-on that was unchecked, and keeps the DOM small since nothing invisible lingers.

Reading offsetWidth forces the browser to flush any pending style changes (a synchronous reflow) before the .bump class is re-added. Without it, if .bump was already present from the previous recalculation, adding the exact same class again is a no-op in the browser's eyes and the CSS transition would not replay.

Add another button.hpc-pill inside #hpcTierPills with its own data-tier and data-price attributes. No JavaScript changes are needed — the click handler is attached via a forEach over all .hpc-pill elements, so a new pill is wired up automatically.

Yes — in recalc(), replace the line computing perSeatCost (tierPrice * seats) with just tierPrice, and either remove the seats slider entirely or keep it purely informational without factoring it into the total.