Docs

Widget version 1.1: adds multi-select, currency/locale formatting and accessibility improvements. v1.0 configs keep working unchanged.

1. Install

Paste this wherever the calculator should appear (WordPress "Custom HTML" block, Wix "Embed code", Squarespace "Code" block, Webflow "Embed", or plain HTML):

<div data-pricerange data-config-src="https://yoursite.com/prices.json"></div>
<script src="https://pricerangekit.com/w/pricerange.js" defer></script>

Or put the config inline:

<div data-pricerange><script type="application/json">{ ...config... }</script></div>

Paid licenses: upload pricerange.pro.js to your own site and point the script tag at it. That gives you no branding and no dependency on us.

2. Config reference

KeyMeaning
title, subtitle, noteText shown in the widget. note appears under the price (e.g. "Final price after inspection").
accentHex color for the price panel, e.g. "#0a7d4f".
currencyCode, localeRecommended. An ISO currency code and a locale, e.g. "currencyCode":"EUR","locale":"de-DE" shows 1.234 € and "GBP","en-GB" shows £1,234. Uses the browser's built-in formatting, so any currency works.
currency, symbolAfterSimple alternative: a symbol (default $), placed before the number, or after it if symbolAfter is true. Ignored when currencyCode is set.
roundToRounding step for the range (default 5).
mode"single" (default): the customer picks one service from a dropdown. "multi": checkboxes, so the customer can pick several services, each with its own quantity, and gets one combined range.
preselect, multiLabelMulti mode only. preselect: indexes of services checked at first (default [0]; [] for none). multiLabel: heading above the checkboxes.
items[]Services: id, label, unit, low/high (price per unit), min (minimum ticket), qtyDefault, qtyMin, qtyMax, qtyStep, optional qtyLabel, optional unitOne (singular unit, e.g. "window", used when the quantity is 1). Leave unit empty for a flat-priced service with no quantity.
modifiers[]{"type":"select","label":...,"options":[{"label":...,"factor":1.35,"add":0}]} or {"type":"checkbox","label":...,"factor":1.2,"add":40}. Optional items:["id"] limits a modifier to certain services.
ctaButton: {"type":"mailto","email":...}, {"type":"tel","phone":...}, {"type":"link","href":...} (appends ?estimate=), or {"type":"webhook","url":...} (shows a lead form and POSTs JSON).
leadFor webhook CTAs: fields (default name, email, phone), submitLabel, thanks.

3. How the math works

For each selected service: low = qty × item.low × (product of factors) + (adds from modifiers scoped to that service), and the same for high. If low is under the service's min, that service's range starts at the minimum. In multi mode the service ranges are added together. Adds from modifiers without an items scope (e.g. "Trip fee +$25") apply once per estimate, not once per service. The total rounds to roundTo. That's it, so the numbers are always explainable.

Example: multi-select in euros

{ "title": "Exterior cleaning", "mode": "multi", "preselect": [0],
  "currencyCode": "EUR", "locale": "de-DE", "roundTo": 10,
  "items": [
    {"id":"drive","label":"Driveway","unit":"m²","low":2,"high":3.5,"min":120,"qtyDefault":50,"qtyMin":10,"qtyMax":400,"qtyStep":5},
    {"id":"windows","label":"Windows","unit":"windows","unitOne":"window","low":6,"high":9,"min":60,"qtyDefault":12}
  ],
  "modifiers": [{"type":"checkbox","label":"Trip outside city","add":25}],
  "cta": {"type":"mailto","email":"you@business.com"} }

4. Collecting leads

With a webhook CTA, submissions POST JSON (name, email, phone, service, quantity, services (a list of each service with its quantity and unit, useful in multi mode), estimate_low, estimate_high, currency, page, submitted_at) to any endpoint that accepts JSON with CORS: Formspree, Make or Zapier webhooks, your own server. Nothing passes through PriceRange Kit.

5. JavaScript API

PriceRange.mount(document.getElementById('calc'), config);
PriceRange.compute(config, {item:0, qty:150, mods:{0:1}})        // single → {lo, hi, lines, empty}
PriceRange.compute(config, {sel:{0:150, 1:12}, mods:{}})           // multi: {serviceIndex: qty}

6. Accessibility

The widget uses real form labels, announces price changes to screen readers (role="status"), has visible keyboard focus and 44 px touch targets, respects "reduced motion", and picks white or black text on your accent color for readable contrast. We test it with axe-core in Chromium, Firefox and WebKit.