Viscosity • quantity first — Separate dynamic from kinematic unit conversion and require an independently supplied matching density before relating the two physical…
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>Viscosity • quantity first</title><style>*{box-sizing:border-box}body{margin:0;background:var(--bg,#f3f7fa);color:var(--ink,#203342);font:16px/1.55 system-ui,Segoe UI,sans-serif}main{max-width:1100px;margin:auto;padding:28px 30px 50px}h1{font-size:clamp(27px,3.5vw,40px);line-height:1.18;letter-spacing:-.035em;margin:8px 0 12px}h2{font-size:21px;line-height:1.35;margin:0 0 16px}h3{font-size:18px}.eyebrow{color:var(--accent,#176783);font-size:12px;letter-spacing:.12em;font-weight:800;text-transform:uppercase}.intro,.muted,small{color:#516272}.intro{margin:0 0 24px}.panel{padding:24px;border:1px solid var(--line,#d6e0e7);border-radius:12px;background:white;margin:18px 0}.grid{display:grid;grid-template-columns:1fr 1fr;gap:22px}.grid>*{min-width:0}.controls{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:16px 0}button,input,textarea,select{font:inherit}button{border:1px solid var(--accent,#176783);background:var(--accent,#176783);color:white;padding:10px 16px;min-height:44px;border-radius:7px;font-weight:700;cursor:pointer}button.secondary{background:white;color:var(--accent,#176783)}button[aria-pressed=true]{background:var(--tint,#e3f2f8);color:var(--ink,#203342);box-shadow:inset 0 0 0 2px var(--accent,#176783)}button:hover{filter:brightness(.96)}:focus-visible{outline:3px solid #d56b00;outline-offset:3px}label{display:block;margin:12px 0 5px;font-size:14px;font-weight:650}input:not([type=checkbox]),select,textarea{width:100%;padding:10px 12px;min-height:44px;border:1px solid #94a5b2;background:white;color:inherit;border-radius:7px}textarea{resize:vertical;min-height:120px}input[type=checkbox]{width:20px;height:20px;margin-right:9px}label.inline{display:flex;align-items:center;font-weight:500;gap:7px}.result{padding:24px;background:var(--tint,#e3f2f8);border:1px solid var(--line,#c8dce7);border-radius:12px}.big{font-size:clamp(30px,5vw,48px);font-weight:780;line-height:1.25;letter-spacing:-.035em;margin:10px 0}.trace{font:15px/1.7 ui-monospace,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}.error{color:#a12820;font-weight:650;min-height:24px}.table-wrap{overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:15px}th,td{text-align:left;border-bottom:1px solid var(--line,#d6e0e7);padding:12px 9px;vertical-align:top}th{font-size:12px;text-transform:uppercase;letter-spacing:.03em;color:#516272}code,pre,.code{font:14px/1.65 ui-monospace,Consolas,monospace}pre{white-space:pre-wrap;overflow-wrap:anywhere;background:var(--tint,#eef3f7);padding:16px;border-radius:6px}details{border-top:1px solid var(--line,#d6e0e7);margin:24px 0;padding-top:16px}summary{cursor:pointer;font-weight:700;min-height:44px}footer{border-top:1px solid var(--line,#d6e0e7);margin-top:26px;padding-top:16px;font-size:13px;color:#516272}.bar{height:16px;background:#dce4ea;border-radius:20px;overflow:hidden;margin:12px 0}.bar span{display:block;height:100%;background:var(--accent,#176783)}[hidden]{display:none!important}@media(max-width:720px){main{padding:20px}.grid{grid-template-columns:1fr;gap:12px}.panel,.result{padding:20px}}@media(max-width:400px){main{padding:16px}.panel,.result{padding:16px}button{padding:10px 13px}}
body{--accent:#176783;--tint:#e3f3fa}.dimension{display:flex;justify-content:space-around;gap:12px;background:#edf4f9;border-radius:8px;padding:20px;text-align:center}.dimension b{font:20px Georgia,serif}@media(max-width:400px){.dimension{font-size:12px;padding:12px}.dimension b{font-size:16px}}</style></head><body><main>
<header><span class="eyebrow">Dimensional workbench</span><h1>Viscosity, with the units intact.</h1><p class="intro">Convert within one quantity family, or relate two quantities with a matching supplied density.</p></header>
<section class="result" aria-live="polite"><span class="eyebrow" id="result-kind">Dynamic viscosity</span><div class="big" id="result">0.012 Pa·s</div><div id="trace" class="trace">12 cP × 0.001 = 0.012 Pa·s</div><p id="result-state">D1 • fictional matching state</p></section>
<div class="grid"><section class="panel"><h2>1. Choose your quantity</h2><div class="controls"><button type="button" id="dynamic" class="secondary">Dynamic μ</button><button type="button" id="kinematic" class="secondary">Kinematic ν</button></div><div class="grid"><div><label for="value">Supplied viscosity</label><input id="value" type="text" value="12"></div><div><label for="from">Source unit</label><select id="from"><option>cP</option><option>P</option><option>Pa·s</option></select></div></div><label for="to">Target unit</label><select id="to"><option>Pa·s</option><option>cP</option><option>P</option></select><label for="sample">Viscosity sample/state tag</label><input id="sample" type="text" value="D1"><label for="state">Temperature / state note</label><input id="state" type="text" value="fictional matching state"><label for="source">Measurement/property source (if known)</label><input id="source" type="text" value=""></section>
<section class="panel"><h2>2. Relate quantities when needed</h2><label class="inline"><input type="checkbox" id="relation">Enable density-dependent relationship</label><p class="muted">Dynamic → kinematic divides by density. Kinematic → dynamic multiplies by density.</p><label for="density">Independently supplied density (kg/m³)</label><input id="density" type="text" value=""><label for="density-tag">Density sample/state tag</label><input id="density-tag" type="text" value=""><label class="inline"><input type="checkbox" id="confirmed">I confirm the sample and state match.</label><div class="controls"><button type="button" id="d1" class="secondary">Load fictional D1</button></div><small>D1 uses 800 kg/m³ as fictional arithmetic input, not a material lookup.</small></section></div>
<div class="controls"><button type="button" id="calculate">Calculate</button><button type="button" id="reset" class="secondary">Reset</button></div><p id="error" class="error" role="alert"></p>
<section class="panel"><h2>Follow the dimensions</h2><div class="dimension"><span>Dynamic<br><b>kg / (m·s)</b></span><span>÷ Density<br><b>kg / m³</b></span><span>= Kinematic<br><b>m² / s</b></span></div><p>Separate D1 worked record: 12 cP = 0.012 Pa·s; 0.012 ÷ 800 = 0.000015 m²/s = 15 cSt. Reverse: 0.000015 × 800 = 0.012 Pa·s = 12 cP.</p><p>Without a relationship: 12 cP = 0.12 P. Separately, 12 cSt = 0.12 St = 0.000012 m²/s.</p></section>
<footer>Educational arithmetic only. Conversion does not adjust temperature, infer material identity, or establish measurement uncertainty. Display uses up to 10 significant digits; the trace retains raw JavaScript values.</footer>
</main><script>const $=id=>document.getElementById(id);const on=(id,e,fn)=>$(id).addEventListener(e,fn);const txt=(id,v)=>$(id).textContent=v;const esc=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));const positive=x=>String(x).trim()!==''&&Number.isFinite(Number(x))&&Number(x)>0;const num=x=>Number(x.toPrecision(10)).toString();function enter(id,fn){on(id,'keydown',e=>{if(e.key==='Enter'&&!e.isComposing&&e.keyCode!==229){e.preventDefault();fn()}})}function selectText(id){$(id).focus();$(id).select()}
const units={dynamic:{'cP':.001,'P':.1,'Pa·s':1},kinematic:{'cSt':.000001,'St':.0001,'m²/s':1}};let family='dynamic';
function viscosity(v,from,to,relation,density,sample,tag,confirmed){if(!positive(v))throw Error('Enter a positive finite viscosity.');const src=units[family],targetFamily=relation?(family==='dynamic'?'kinematic':'dynamic'):family,dst=units[targetFamily];if(!Object.hasOwn(src,from)||!Object.hasOwn(dst,to))throw Error('Choose supported units in the current quantity families.');let base=Number(v)*src[from];const rawBase=base;if(relation){if(!positive(density))throw Error('Supply a positive finite density in kg/m³.');if(!sample.trim()||!tag.trim()||sample.trim()!==tag.trim()||!confirmed)throw Error('Confirm matching, nonblank sample/state tags.');base=family==='dynamic'?base/Number(density):base*Number(density);}const result=base/dst[to];if(!Number.isFinite(result)||result<=0||!Number.isFinite(base)||base<=0)throw Error('Value is outside the supported numeric range.');return {result,base,rawBase,targetFamily}}
function pending(){txt('result','Pending calculation');txt('trace','Inputs changed. Calculate to review the current trace.');txt('result-state','');txt('error','')}
function menus(){const source=Object.keys(units[family]),dest=Object.keys(units[$('relation').checked?(family==='dynamic'?'kinematic':'dynamic'):family]);$('from').innerHTML=source.map(u=>'<option>'+u+'</option>').join('');$('to').innerHTML=dest.map(u=>'<option>'+u+'</option>').join('');$('from').value=source[0];$('to').value=dest[dest.length-1];$('dynamic').setAttribute('aria-pressed',String(family==='dynamic'));$('kinematic').setAttribute('aria-pressed',String(family==='kinematic'));pending()}
function calculate(){try{const r=viscosity($('value').value,$('from').value,$('to').value,$('relation').checked,$('density').value,$('sample').value,$('density-tag').value,$('confirmed').checked);if($('relation').checked&&!$('state').value.trim())throw Error('Record the temperature/state note before relating quantities.');txt('result',num(r.result)+' '+$('to').value);txt('result-kind',r.targetFamily==='dynamic'?'Dynamic viscosity μ':'Kinematic viscosity ν');txt('trace',$('value').value+' '+$('from').value+' × '+units[family][$('from').value]+' = '+r.rawBase+' '+(family==='dynamic'?'Pa·s':'m²/s')+($('relation').checked?'\n'+r.rawBase+(family==='dynamic'?' ÷ ':' × ')+$('density').value+' = '+r.base+' '+(r.targetFamily==='dynamic'?'Pa·s':'m²/s'):'')+'\nRaw target: '+r.result+' '+$('to').value);txt('result-state',[$('sample').value,$('state').value,$('source').value].filter(Boolean).join(' • '));txt('error','')}catch(e){txt('result','No accepted result');txt('trace','');txt('result-state','');txt('error',e.message)}}
on('dynamic','click',()=>{family='dynamic';menus()});on('kinematic','click',()=>{family='kinematic';menus()});on('relation','change',menus);
for(const id of ['value','from','to','sample','state','source','density','density-tag','confirmed']){on(id,'input',pending);on(id,'change',pending)}on('calculate','click',calculate);enter('value',calculate);enter('density',calculate);
on('d1','click',()=>{family='dynamic';$('relation').checked=true;menus();$('value').value='12';$('from').value='cP';$('to').value='cSt';$('density').value='800';$('sample').value='D1';$('density-tag').value='D1';$('state').value='fictional matching state';$('source').value='';$('confirmed').checked=true;pending()});
on('reset','click',()=>{family='dynamic';$('relation').checked=false;menus();$('value').value='12';$('to').value='Pa·s';for(const id of ['density','density-tag','source'])$(id).value='';$('sample').value='D1';$('state').value='fictional matching state';$('confirmed').checked=false;txt('result','Ready to calculate');txt('trace','');txt('error','');txt('result-state','')});$('dynamic').setAttribute('aria-pressed','true');$('kinematic').setAttribute('aria-pressed','false');calculate();
</script></body></html>