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
| Key | Meaning |
|---|---|
title, subtitle, note | Text shown in the widget. note appears under the price (e.g. "Final price after inspection"). |
accent | Hex color for the price panel, e.g. "#0a7d4f". |
currencyCode, locale | Recommended. 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, symbolAfter | Simple alternative: a symbol (default $), placed before the number, or after it if symbolAfter is true. Ignored when currencyCode is set. |
roundTo | Rounding 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, multiLabel | Multi 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. |
cta | Button: {"type":"mailto","email":...}, {"type":"tel","phone":...}, {"type":"link","href":...} (appends ?estimate=), or {"type":"webhook","url":...} (shows a lead form and POSTs JSON). |
lead | For 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":"[email protected]"} }
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.