Concentration · Choose the Basis — Choose mass/mass, volume/volume or grams-per100mL basis before computing a supplied fictional composition record, and preserve the…

HTML for this example

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

<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Concentration · Choose the Basis</title><style>:root{--bg:#f1f8f9;--ink:#153c48;--accent:#187584;--line:#bfd4da;--wash:#e0f0f2;}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:16px/1.5 Arial,Helvetica,sans-serif}main{max-width:1100px;margin:auto;padding:26px 24px 54px}h1,h2,h3,p{margin:0}h1{font-size:clamp(27px,4vw,39px);line-height:1.15;letter-spacing:-.03em}h2{font-size:23px}h3{font-size:18px}header{margin-bottom:18px}header p{margin-top:8px}.eyebrow{font-size:12px;letter-spacing:.12em;text-transform:uppercase;font-weight:bold;color:var(--accent)}button,input,textarea,select{font:inherit}button{min-height:44px;padding:9px 15px;background:var(--accent);color:#fff;border:1px solid var(--accent);border-radius:6px;cursor:pointer;font-weight:bold}button.secondary{background:#fff;color:var(--accent)}button:disabled{opacity:.45;cursor:default}button[aria-pressed=true]{box-shadow:inset 0 0 0 2px currentColor}input,textarea,select{width:100%;min-height:44px;border:1px solid #919cad;background:#fff;color:var(--ink);border-radius:5px;padding:9px 11px}textarea{resize:vertical;min-height:94px}input[type=checkbox]{width:20px;min-height:20px;vertical-align:middle;margin-right:8px}label{display:block;margin:11px 0 5px;font-size:14px;font-weight:bold}:focus-visible{outline:3px solid #dc8c21;outline-offset:3px}.actions{display:flex;flex-wrap:wrap;gap:9px;margin:14px 0}.panel{background:#fff;border:1px solid var(--line);border-radius:10px;padding:22px;min-width:0}.columns{display:grid;grid-template-columns:1.45fr 1fr;gap:22px}.two{display:grid;grid-template-columns:1fr 1fr;gap:16px}.three{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}.muted{color:#536176;font-size:14px}.notice{margin:14px 0;background:var(--wash);border-left:4px solid var(--accent);padding:13px 16px;overflow-wrap:anywhere}.error{color:#a42d25;font-weight:bold}.below{margin-top:28px;padding-top:22px;border-top:1px solid var(--line)}.below>p{margin:9px 0}.big{font-size:clamp(34px,5vw,56px);line-height:1.15;font-weight:bold}.tag{font-size:12px;padding:4px 8px;border-radius:4px;background:var(--wash)}.row{display:flex;gap:10px;align-items:end}.row>*{min-width:0}.row .unit{width:110px;flex:none}pre{white-space:pre-wrap;overflow-wrap:anywhere;font:13px/1.6 ui-monospace,monospace}table{border-collapse:collapse;width:100%;font-size:14px}th,td{text-align:left;vertical-align:top;padding:9px;border-bottom:1px solid var(--line)}th{font-size:12px;text-transform:uppercase}.scroll{overflow-x:auto}.foot{font-size:14px;color:#536176;margin-top:24px}summary{cursor:pointer;font-weight:bold;min-height:44px;padding:10px 0}details{margin:18px 0}.readonly{font:13px/1.5 ui-monospace,monospace;min-height:170px}.hash{overflow-wrap:anywhere;font:12px/1.6 ui-monospace,monospace}[hidden]{display:none!important}@media(max-width:800px){.columns{grid-template-columns:1fr}}@media(max-width:520px){main{padding:18px 14px 40px}.panel{padding:17px}.two,.three{grid-template-columns:1fr}.actions button{flex-grow:1}td,th{padding:7px}.row .unit{width:90px}}.basis-strip{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin:16px 0}.basis-strip button{background:#fff;color:#176573;text-align:left;padding:16px}.basis-strip button[aria-pressed=true]{background:#187584;color:white;box-shadow:none}.basis-strip span{display:block;font-size:13px;font-weight:normal}.result-box{background:#153f4d;color:#fff;padding:26px;border-radius:12px}.result-box .eyebrow{color:#b6e1e3}.result-box .big{margin:10px 0}.result-box p{margin-top:10px}#basis-diagram{width:100%;height:100px;margin-top:15px}.unit-note{font-size:14px;color:#d2e5e7}.compare{border-top:3px solid var(--accent);background:white;padding:16px}.compare strong{font-size:25px}.formula{font:16px/1.8 ui-monospace,monospace;overflow-wrap:anywhere}@media(max-width:520px){.basis-strip{gap:5px}.basis-strip button{padding:10px;font-size:14px}.basis-strip span{font-size:12px}}</style></head><body><main><header><p class="eyebrow">Fictional composition records · arithmetic helper</p><h1>Choose the basis. Keep it in the answer.</h1><p>Divide the component quantity by the final solution quantity, with compatible units.</p></header><nav class="basis-strip" aria-label="Concentration basis"><button type="button" id="mode-mm">Mass / mass<span>Output: % m/m</span></button><button type="button" id="mode-vv">Volume / volume<span>Output: % v/v</span></button><button type="button" id="mode-mv">Mass / volume<span>Output: g per 100 mL</span></button></nav><div class="columns"><section class="result-box" aria-live="polite"><p class="eyebrow" id="result-label">C1 · mass / mass</p><div class="big" id="answer">4% m/m</div><p class="formula" id="calculation">(5 g / 125 g) × 100 = 4% m/m</p><p id="normalized">Normalized: 5 g component / 125 g final solution</p><svg id="basis-diagram" viewBox="0 0 400 100" role="img" aria-label="Basis-labeled concentration diagram"></svg><p class="unit-note" id="interpretation">4 g component per 100 g of final solution.</p></section><section class="panel"><h2 id="form-title">Mass / mass inputs</h2><label id="component-label" for="component">Component mass</label><div class="row"><input id="component" value="5" inputmode="decimal" aria-describedby="error"><select id="component-unit" class="unit" aria-label="Component mass unit"><option value="g">g</option><option value="mg">mg</option><option value="kg">kg</option></select></div><label id="solution-label" for="solution">Final solution mass</label><div class="row"><input id="solution" value="125" inputmode="decimal" aria-describedby="error"><select id="solution-unit" class="unit" aria-label="Final solution mass unit"><option value="g">g</option><option value="mg">mg</option><option value="kg">kg</option></select></div><div class="actions"><button type="button" id="calculate">Calculate</button><button type="button" id="load" class="secondary">Load supplied record</button><button type="button" id="clear" class="secondary">Clear</button></div><p id="error" class="error" role="status"></p><p class="muted">A basis change clears the previous record. Input edits clear the old answer until you calculate again.</p></section></div><section class="below"><h2>Same numeral. Three separate records.</h2><div class="three" style="margin-top:14px"><article class="compare"><span class="tag">C1 · 5 g / 125 g</span><p><strong>4% m/m</strong></p><p>Mass fraction of the final solution.</p></article><article class="compare"><span class="tag">C2 · 5 mL / 125 mL</span><p><strong>4% v/v</strong></p><p>Volume basis, using supplied final volume.</p></article><article class="compare"><span class="tag">C3 · 5 g / 125 mL</span><p><strong>4 g / 100 mL</strong></p><p>Mixed-unit convention, labeled 4% m/v.</p></article></div><p>C1 can also be entered as 5000 mg / 0.125 kg. Its optional closed accounting record is 5 g component + 120 g solvent = 125 g final solution; 120 g is not the denominator.</p><p class="foot">These are fictional supplied composition records, not a mixing recipe or a measurement. No density or cross-basis conversion is supplied. Volume-mode component ≤ final volume is this exercise’s scope rule, not a volume-additivity law. Mixed mass/volume results have no universal 100% ceiling. No dosing, preparation, laboratory or safety decision is supported.</p></section></main><script>'use strict';const $=id=>document.getElementById(id);let mode='mm';const units={mass:{g:1,mg:.001,kg:1000},volume:{mL:1,L:1000}};const info={mm:{name:'Mass / mass',num:'mass',den:'mass',baseN:'g',baseD:'g',suffix:'% m/m',record:'C1'},vv:{name:'Volume / volume',num:'volume',den:'volume',baseN:'mL',baseD:'mL',suffix:'% v/v',record:'C2'},mv:{name:'Mass / volume',num:'mass',den:'volume',baseN:'g',baseD:'mL',suffix:'% m/v',record:'C3'}};
const fmt=v=>Object.is(v,-0)||v===0?'0':Number(v.toPrecision(10)).toString();
function evaluate(a,au,d,du,m=mode){const q=info[m];if(a.trim()===''||d.trim()==='')return {error:'Enter both the component and final solution quantities.'};if(!/^[+]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/.test(a.trim())||!/^[+]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/.test(d.trim()))return {error:'Use finite, nonnegative numbers with no unit text in the value fields.'};const n=Number(a)*units[q.num][au],total=Number(d)*units[q.den][du];if(!Number.isFinite(n)||!Number.isFinite(total)||n<0||total<0)return {error:'Use finite, nonnegative quantities and compatible units.'};if(total===0)return {error:'Final solution quantity must be greater than zero.'};if(m==='mm'&&n>total)return {error:'Inconsistent mass record: component mass exceeds final solution mass.'};if(m==='vv'&&n>total)return {error:'Outside this volume fixture scope: component volume exceeds final solution volume.'};const value=n/total*100;if(!Number.isFinite(value))return {error:'The normalized result exceeds the finite calculation range.'};return {n,total,value};}
function invalidate(){const r=evaluate($('component').value,$('component-unit').value,$('solution').value,$('solution-unit').value);$('answer').textContent='—';$('calculation').textContent='No current calculation';$('normalized').textContent='';$('interpretation').textContent='Calculate the revised inputs to produce a basis-labeled answer.';$('basis-diagram').innerHTML='';$('error').textContent=r.error||'Inputs changed — calculate again.';}
function configure(m){mode=m;const q=info[m];for(const k of ['mm','vv','mv'])$('mode-'+k).setAttribute('aria-pressed',String(k===m));for(const [id,kind] of [['component-unit',q.num],['solution-unit',q.den]]){$(id).innerHTML=Object.keys(units[kind]).map(u=>'<option value="'+u+'">'+u+'</option>').join('');$(id).value=Object.keys(units[kind])[0];$(id).setAttribute('aria-label',(id==='component-unit'?'Component ':'Final solution ')+kind+' unit');}$('component-label').textContent='Component '+q.num;$('solution-label').textContent='Final solution '+q.den;$('form-title').textContent=q.name+' inputs';$('result-label').textContent=q.record+' · '+q.name.toLowerCase();clear();}
function clear(){$('component').value='';$('solution').value='';invalidate();$('error').textContent='';}
function calculate(){const q=info[mode],r=evaluate($('component').value,$('component-unit').value,$('solution').value,$('solution-unit').value);if(r.error){invalidate();$('error').textContent=r.error;return;}$('error').textContent='';$('answer').textContent=mode==='mv'?fmt(r.value)+' g per 100 mL':fmt(r.value)+q.suffix;$('calculation').textContent='('+fmt(r.n)+' '+q.baseN+' / '+fmt(r.total)+' '+q.baseD+') × 100 = '+fmt(r.value)+q.suffix;$('normalized').textContent='Entered: '+$('component').value+' '+$('component-unit').value+' / '+$('solution').value+' '+$('solution-unit').value+' → normalized: '+fmt(r.n)+' '+q.baseN+' / '+fmt(r.total)+' '+q.baseD+' final solution';$('interpretation').textContent=fmt(r.value)+' '+q.baseN+' component per 100 '+q.baseD+' of final solution.'+(mode==='mv'?' Mixed-unit label: '+fmt(r.value)+'% m/v.':'');$('basis-diagram').innerHTML=mode==='mv'?'<rect x="5" y="20" width="390" height="60" rx="7" fill="#ffffff" fill-opacity=".12"/><text x="20" y="45" fill="white" font-size="15">Final solution volume: 100 mL</text><text x="20" y="68" fill="#bce8df" font-size="17">Component mass: '+fmt(r.value)+' g</text>':'<rect x="5" y="25" width="390" height="32" rx="5" fill="#ffffff" fill-opacity=".18"/><rect x="5" y="25" width="'+(390*r.value/100)+'" height="32" rx="5" fill="#72d4c0"/><text x="5" y="82" fill="white" font-size="14">'+fmt(r.value)+' of 100 '+q.baseD+' · component / final solution</text>';}
function load(){$('component').value='5';$('solution').value='125';$('component-unit').value=info[mode].baseN;$('solution-unit').value=info[mode].baseD;calculate();}
for(const m of ['mm','vv','mv'])$('mode-'+m).addEventListener('click',()=>configure(m));for(const id of ['component','solution']){$(id).addEventListener('input',invalidate);$(id).addEventListener('keydown',e=>{if(e.key==='Enter'&&!e.isComposing&&e.keyCode!==229){e.preventDefault();calculate();}});}for(const id of ['component-unit','solution-unit'])$(id).addEventListener('change',invalidate);$('calculate').addEventListener('click',calculate);$('clear').addEventListener('click',clear);$('load').addEventListener('click',load);globalThis.app={evaluate,configure,load,calculate,fmt,get:()=>mode};configure('mm');load();</script></body></html>