Discount calculator

HTML for this example

Select the code and use your copy shortcut. Changes in this preview are temporary.

<!doctype html><html lang="en-US"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Discount calculator</title><style>*{box-sizing:border-box}html{color-scheme:light}body{margin:0;background:var(--bg,#f3f4f5);color:var(--ink,#192838);font:16px/1.5 system-ui,-apple-system,Segoe UI,sans-serif}main{max-width:1040px;margin:auto;padding:28px 28px 48px}header{margin-bottom:20px}h1{font-size:clamp(25px,4vw,36px);line-height:1.15;letter-spacing:-.035em;margin:6px 0 9px}h2{font-size:21px;line-height:1.25;margin:0 0 16px}h3{font-size:17px;margin:0 0 10px}p{margin:8px 0 16px}.eyebrow{font-size:12px;font-weight:750;text-transform:uppercase;letter-spacing:.12em;color:var(--accent,#275e86)}.subtle,small{color:#536170}.panel{padding:24px;background:white;border:1px solid var(--line,#d5dce2);border-radius:12px;margin:18px 0}.result{background:var(--result,#132f47);color:white;padding:26px;border-radius:14px;margin:18px 0}.result .subtle,.result small{color:#d7e3ed}.big{font-size:clamp(32px,6vw,56px);font-weight:750;line-height:1.15;letter-spacing:-.035em;margin:8px 0 14px;overflow-wrap:anywhere}.mono,pre,code{font-family:ui-monospace,Consolas,monospace;font-variant-numeric:tabular-nums}.mono{overflow-wrap:anywhere}pre{white-space:pre-wrap;overflow-wrap:anywhere;font-size:14px;line-height:1.7;margin:0}.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:20px}.triple{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}.fields{display:flex;gap:16px;flex-wrap:wrap;align-items:end}.fields>label{flex:1;min-width:130px}label{display:block;font-size:14px;font-weight:650}input,select,textarea{display:block;width:100%;font:inherit;font-size:16px;color:inherit;background:white;border:1px solid #a9b4bf;border-radius:6px;padding:11px 12px;margin-top:6px;min-width:0}input[type=checkbox]{display:inline;width:auto;margin:0 9px 0 0;accent-color:var(--accent,#275e86)}textarea{min-height:100px;resize:vertical}button{font:inherit;font-weight:650;padding:10px 17px;border:1px solid var(--accent,#275e86);background:var(--accent,#275e86);color:white;border-radius:6px;cursor:pointer;min-height:44px}button.secondary{background:white;color:var(--accent,#275e86)}button:disabled{opacity:.5;cursor:default}button:hover{filter:brightness(.94)}:focus-visible{outline:3px solid #e9a733;outline-offset:3px}.actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:18px}.error{color:#a32325;font-weight:650;min-height:0}.error:not(:empty){padding:12px;border-left:4px solid #ae3237;background:#fff2f0;margin:14px 0}[hidden]{display:none!important}fieldset{border:1px solid #ccd4da;border-radius:8px;padding:18px;margin:0;min-width:0}legend{padding:0 7px;font-weight:750}table{width:100%;border-collapse:collapse;font-size:14px;table-layout:fixed}th{text-align:left;font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:#536170}td,th{padding:12px 9px;border-bottom:1px solid #d9dfe4;vertical-align:top;overflow-wrap:anywhere}th:first-child,td:first-child{padding-left:0}dl{margin:0}dl>div{padding:10px 0;border-bottom:1px solid #d9dfe4}dt{font-size:13px;color:#536170}dd{margin:4px 0 0;font-weight:650}details{margin:20px 0;padding:18px 0;border-top:1px solid #cbd2d8}summary{cursor:pointer;font-weight:700}details p{margin-top:16px}a{color:var(--accent,#275e86)}.tag{display:inline-block;font-size:12px;font-weight:750;letter-spacing:.03em;border:1px solid currentColor;border-radius:5px;padding:4px 8px}.rule{border-top:1px solid #d9dfe4;padding-top:20px}.line{height:30px;border-bottom:1px solid #aab4be}.split-stat{display:flex;justify-content:space-between;gap:20px;align-items:baseline;flex-wrap:wrap}.result h2{color:white}.result table td,.result table th{border-color:#ffffff35;color:white}.result .eyebrow{color:#d7e3ed}.readout{padding:16px;background:#f0f4f6;border-radius:6px;margin-top:14px}.empty{padding:22px;border:1px dashed #a6b0ba;border-radius:8px;color:#536170;margin:18px 0}footer{font-size:13px;color:#536170;margin-top:24px}@media(max-width:720px){main{padding:20px}.panel,.result{padding:20px}.grid{gap:14px}.triple{gap:10px}}@media(max-width:540px){main{padding:16px}.grid{grid-template-columns:1fr}.panel,.result{padding:18px;border-radius:9px}.fields{gap:12px}.fields>label{min-width:100px}h1{font-size:27px}th,td{padding:10px 5px;font-size:13px}.triple{grid-template-columns:1fr}.big{font-size:38px}}.savings-bar{height:12px;border-radius:9px;background:#ffffff30;margin-top:22px;overflow:hidden}.savings-bar span{display:block;height:100%;background:#f3d58b;width:0}</style></head><body style="--bg:#f5f1e7;--accent:#745122;--result:#493c28"><main><header><div class="eyebrow">Working example</div><h1>Discount calculator</h1><p class="subtle">Round each discount once, then subtract it in whole cents.</p></header><section id="result" class="result" aria-live="polite"><div class="eyebrow">Remaining item price · USD</div><div class="big" id="sale"></div><div class="split-stat"><span id="savings"></span><span id="effective"></span></div><div class="savings-bar" aria-hidden="true"><span id="bar"></span></div></section><p id="error" class="error" role="alert"></p><p id="empty" class="empty" hidden>Inputs changed. Calculate to see the current result.</p><form id="form" class="panel" novalidate><div class="fields"><label for="price">Original price · USD<input id="price" value="19.99" inputmode="decimal" autocomplete="off"></label><label for="rate">First discount · % off<input id="rate" value="15" inputmode="decimal" autocomplete="off"></label></div><p><label><input type="checkbox" id="second">Apply a second discount to the remainder</label></p><div id="secondwrap" hidden><label for="rate2">Second discount · % off<input id="rate2" value="10" inputmode="decimal" autocomplete="off"></label></div><div class="actions"><button>Calculate price</button><button type="button" id="stack" class="secondary">Try $100 → 20% → 10%</button></div></form><section class="panel"><h2>Calculation record</h2><pre id="trace"></pre></section><details open><summary>Rounding policy and scope</summary><p>Calculate the raw discount, round it to cents with half cents up, then subtract that rounded amount. Price: 0–1,000,000 with at most two decimals. Rates: 0–100 with at most six decimals. Enter numbers with a decimal point; no currency symbols, commas, or exponents.</p><p>Fictional USD inputs. Tax, shipping, fees, offer terms, and eligibility are unknown and excluded. This is an item-price calculation, not a checkout total.</p></details><footer>Local review copy · No data is sent or saved.</footer></main><script>const $=id=>document.getElementById(id);const esc=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));function round(n,d){return (2n*n+d)/(2n*d)}function dec(n,d,p=6){let x=round(n*10n**BigInt(p),d).toString().padStart(p+1,'0');return p?x.slice(0,-p)+'.'+x.slice(-p):x}function exact(n,d){let a=n/d,r=n%d,s='';for(let i=0;r&&i<36;i++){r*=10n;s+=r/d;r%=d}return a+(s?'.'+s:'')+(r?'…':'')}function num(s,name,max=1000000000,places=9,zero=false){if(!new RegExp('^[0-9]+(?:[.][0-9]{1,'+places+'})?$').test(s)||s.length>35)throw Error(name+': use a '+(zero?'nonnegative':'positive')+' dot-decimal number.');let [a,b='']=s.split('.'),d=10n**BigInt(b.length),n=BigInt(a+b);if((zero?n<0n:n<=0n)||n>BigInt(max)*d)throw Error(name+': value outside the supported range.');return {n,d,v:Number(n)/Number(d)}}function clear(){ $('result').hidden=true;$('trace').textContent='';$('empty').hidden=false;$('error').textContent='';}function reveal(){ $('result').hidden=false;$('empty').hidden=true;$('error').textContent='';}function error(e){clear();$('error').textContent=e.message;}function bindForm(run){$('form').addEventListener('submit',e=>{e.preventDefault();run()});$('form').addEventListener('input',clear);$('form').addEventListener('change',clear);}

function priceCents(s){const r=num(s,'Original price',1000000,2,true);return r.n*100n/r.d}function rateValue(s,name){return num(s,name,100,6,true)}function discount(c,s){const r=rateValue(s,'Discount rate'),n=c*r.n,d=100n*r.d,save=round(n,d);return {basis:c,save,left:c-save,raw:exact(n,d*100n),rate:s}}function calc(p,r,r2=null){const cents=priceCents(p);rateValue(r,'First discount rate');if(r2!==null)rateValue(r2,'Second discount rate');const stages=[discount(cents,r)];if(r2!==null)stages.push(discount(stages[0].left,r2));const left=stages.at(-1).left;return {cents,left,saved:cents-left,stages}}const money=c=>'$'+dec(c,100n,2);
function run(){try{const r=calc($('price').value,$('rate').value,$('second').checked?$('rate2').value:null);reveal();$('sale').textContent=money(r.left);$('savings').textContent='You save '+money(r.saved)+' of '+money(r.cents);$('effective').textContent=r.cents?'Effective savings ≈ '+dec(r.saved*100n,r.cents,4)+'%':'Effective savings % undefined (zero original price)';$('bar').style.width=(r.cents?Number(r.saved)*100/Number(r.cents):0)+'%';$('trace').textContent=r.stages.map((s,i)=>'Stage '+(i+1)+' | Basis '+money(s.basis)+' × '+s.rate+'%\nRaw discount $'+s.raw+' → rounded '+money(s.save)+'\n'+s.basis+' = '+s.save+' + '+s.left+' cents\nRemainder '+money(s.left)).join('\n\n')+'\n\nTotal savings '+money(r.saved)+'; remaining '+money(r.left)+'.';}catch(e){error(e)}}bindForm(run);$('second').addEventListener('change',()=>{$('secondwrap').hidden=!$('second').checked});$('stack').onclick=()=>{$('price').value='100';$('rate').value='20';$('second').checked=true;$('secondwrap').hidden=false;$('rate2').value='10';run()};run();</script>
<script>
// Local calculation controls: no native form submission or sandbox permissions required.
(()=>{const form=document.getElementById('form');if(!form)throw Error('Calculation form missing');const dispatch=()=>form.dispatchEvent(new Event('submit',{bubbles:true,cancelable:true}));const buttons=[...form.querySelectorAll('button')].filter(button=>button.type==='submit');if(buttons.length!==1)throw Error('Expected one calculation control');for(const button of buttons){button.type='button';button.addEventListener('click',dispatch);}form.addEventListener('keydown',event=>{if(event.key==='Enter'&&!event.isComposing&&['INPUT','SELECT'].includes(event.target.tagName)){event.preventDefault();dispatch();}});})();
</script>
</body></html>