Notebook variants: one coherent selection
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>Notebook variants: one coherent selection</title><style>*{box-sizing:border-box}:root{--ink:#26353e;--muted:#526771;--accent:#285f77;--line:#d0dade;--soft:#edf3f5;color-scheme:light}body{margin:0;background:#f2f5f5;color:var(--ink);font:16px/1.5 system-ui,-apple-system,"Segoe UI",sans-serif}main{max-width:1120px;margin:auto;padding:28px 32px 64px}header{margin-bottom:20px}h1{font-size:clamp(1.5rem,3vw,2.1rem);line-height:1.2;letter-spacing:-.035em;margin:0 0 8px}header p{margin:0}h2{font-size:1.2rem;line-height:1.3;margin:0 0 16px}h3{font-size:1rem;margin:0 0 8px}p{margin:9px 0 16px}.panel{background:white;border:1px solid var(--line);border-radius:10px;padding:24px}.grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:24px}.fields{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px}.below{margin-top:24px}.muted,small{color:var(--muted)}.big{font-size:clamp(2rem,5vw,3.6rem);font-weight:750;line-height:1.14;letter-spacing:-.04em;margin:9px 0;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.subresult{font-size:1.2rem;font-weight:700}.formula{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;overflow-wrap:anywhere}label{display:block;font-size:.9rem;font-weight:650}input,select,textarea,button{font:inherit}input,select,textarea{display:block;width:100%;min-width:0;margin-top:6px;padding:10px 12px;border:1px solid #879da8;border-radius:6px;background:white;color:var(--ink)}input,select{min-height:46px}textarea{line-height:1.6;resize:vertical}button{min-height:44px;padding:9px 15px;border:1px solid var(--accent);border-radius:6px;background:var(--accent);color:white;font-weight:650;cursor:pointer}button.secondary{background:white;color:var(--accent)}button:disabled{opacity:.5;cursor:not-allowed}:focus-visible{outline:3px solid #bf6a22;outline-offset:3px}.actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px}.check{display:flex;align-items:flex-start;gap:10px;font-weight:500}.check input{width:20px;height:20px;min-height:20px;margin:2px 0 0;flex:none;accent-color:var(--accent)}.badge{display:inline-block;background:var(--soft);color:var(--accent);font-size:.78rem;font-weight:700;padding:4px 9px;border-radius:24px}.error{min-height:24px;color:#a52f29;font-weight:650;margin:8px 0 0}.status{font-size:.88rem;color:var(--muted)}.note{background:#fff3de;border-left:3px solid #b47e3d;padding:12px 16px;color:#63491f}.rule{border-top:1px solid var(--line);margin:18px 0}table{border-collapse:collapse;width:100%;font-size:.92rem}th{font-size:.82rem;color:var(--muted);text-align:left}th,td{border-bottom:1px solid var(--line);padding:10px 8px;vertical-align:top}th:first-child,td:first-child{padding-left:0}details{border-top:1px solid var(--line);padding:16px 0}summary{font-weight:650;cursor:pointer}svg{display:block;width:100%;height:auto}figure{margin:0}figcaption{font-size:.84rem;color:var(--muted);margin-top:10px}pre{white-space:pre-wrap;overflow-wrap:anywhere;font:.86rem/1.6 ui-monospace,Consolas,monospace;background:#eaf0f3;border-radius:6px;padding:18px}ol{padding-left:24px}li{margin:7px 0}[hidden]{display:none!important}@media(max-width:720px){main{padding:22px 20px 44px}.grid{grid-template-columns:1fr;gap:18px}.panel{padding:20px}}@media(max-width:400px){main{padding:18px 16px 36px}.panel{padding:16px}.big{font-size:2.3rem}.fields{gap:12px}th,td{padding:8px 5px}}@media print{body{background:white}main{padding:0;max-width:none}.actions,button{display:none}.panel{break-inside:avoid}details>*{display:block}}:root{--accent:#6e5944}body{background:#f5f1eb}.product-layout{display:grid;grid-template-columns:1fr 1fr;gap:32px;background:white;padding:26px;border:1px solid #d9cdbb}.product-media{background:#efe9df;padding:15px;border-radius:6px;align-self:start}.product-media svg{max-height:310px}.product-details h2{font-size:1.5rem;margin:16px 0 0}.product-details .big{font-size:2.8rem}.no-media{display:grid;place-items:center;min-height:270px;color:#6d665d;border:1px dashed #ac9e87}.product-media figcaption{padding:0 8px 8px}@media(max-width:720px){.product-layout{grid-template-columns:1fr;gap:20px;padding:20px}.product-media svg{max-height:190px}.no-media{min-height:150px}}@media(max-width:720px){.product-details{order:-1}}</style></head><body><main><header><h1>Notebook variants: one coherent selection</h1><p>Fictional P01 state review · selectors use local mock records, with no cart or checkout.</p></header>
<section class="product-layout"><figure class="product-media"><div id="media"></div><figcaption id="mediaLabel">M01 · original schematic placeholder, not a product photo</figcaption></figure><section class="product-details"><span class="badge">P01 · Notebook</span><h2 id="variantTitle">Amber / 48 pages</h2><div id="price" class="big" aria-live="polite">$8.00</div><p id="variantId" class="formula">Selected mock ID: V01</p><div class="fields"><label for="color">Color<select id="color"><option>Amber</option><option>Blue</option></select></label><label for="pages">Pages<select id="pages"><option>48</option><option>96</option></select></label></div><p id="availability" class="note" role="status">Orderable under the supplied mock rule only.</p></section></section>
<section class="below panel"><h2>Supplied mock variant manifest</h2><table><thead><tr><th>ID</th><th>Options</th><th>USD / media</th><th>Mock rule</th></tr></thead><tbody><tr><td>V01</td><td>Amber / 48</td><td>$8 / M01</td><td>Orderable</td></tr><tr><td>V02</td><td>Blue / 48</td><td>$9 / M02</td><td>Not orderable</td></tr><tr><td>V03</td><td>Amber / 96</td><td>$12 / M03</td><td>Orderable</td></tr><tr><td>None</td><td>Blue / 96</td><td>No price or variant media</td><td>No matching variant</td></tr></tbody></table></section>
<details class="below"><summary>Native Shopify implementation remains separate</summary><p>product.json references a section; that section owns HTML/Liquid content, media, and the real product form. The local mock does not install a theme, fetch store data, validate stock, create a cart, or submit an order.</p><p>Confirm actual theme/version, section and handlers, real variant/option-value IDs and order, currency formatting, quantity behavior, media fallback, and form behavior in the store. Test valid direct links and nonexistent combinations. Mock V01–V03 are not Shopify URL IDs. The documentation’s quantity prose and min=1 example differ, so no quantity rule is inferred here.</p><p>Transition checks: Amber/48 → Amber/96 updates V03/$12/M03; Blue/48 retains its own V02/$9/M02 but is not orderable; Blue/96 retains the requested options and clears ID/price/variant media; Amber/48 recovers V01. Rendering this HTML does not certify native theme compatibility.</p></details></main><script>const $=id=>document.getElementById(id),put=(id,s)=>{$(id).textContent=s};
function esc(s){return String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));}
function gcd(a,b){a=a<0n?-a:a;b=b<0n?-b:b;while(b){[a,b]=[b,a%b]}return a||1n;}
function R(n,d=1n){n=BigInt(n);d=BigInt(d);if(d===0n)throw Error('Undefined denominator.');if(d<0n){n=-n;d=-d}const g=gcd(n,d);return {n:n/g,d:d/g};}
function mul(a,b){return R(a.n*b.n,a.d*b.d)}function div(a,b){return R(a.n*b.d,a.d*b.n)}function add(a,b){return R(a.n*b.d+b.n*a.d,a.d*b.d)}function sub(a,b){return R(a.n*b.d-b.n*a.d,a.d*b.d)}function cmp(a,b){const x=a.n*b.d-b.n*a.d;return x<0n?-1:x>0n?1:0}
function parse(s,{max=1000000,positive=false,places=6}={}){const t=String(s).trim();if(!t)throw Error('Enter a value; blank is not zero.');const re=new RegExp('^[0-9]+(?:\.[0-9]{1,'+places+'})?$');if(!re.test(t))throw Error('Use digits and a US decimal point, up to '+places+' places; no signs, commas, or exponents.');const v=Number(t);if(!Number.isFinite(v)||v>max||(positive&&v===0))throw Error('Value must be '+(positive?'greater than zero':'nonnegative')+' and at most '+max+'.');const [a,b='']=t.split('.');return R(BigInt(a+b),10n**BigInt(b.length));}
function format(a,places=6){const neg=a.n<0n,n=neg?-a.n:a.n,scale=10n**BigInt(places),v=n*scale,q=v/a.d,rem=v%a.d,k=q+(rem*2n>=a.d?1n:0n);const f=(k%scale).toString().padStart(places,'0').replace(/0+$/,'');return {text:(neg&&k!==0n?'-':'')+(k/scale)+(f?'.'+f:''),approx:rem!==0n};}
function exact(a){let d=a.d;while(d%2n===0n)d/=2n;while(d%5n===0n)d/=5n;if(d!==1n)return a.n+'/'+a.d;let places=0,p=1n;while(p%a.d!==0n&&places<40){p*=10n;places++}return format(a,places).text;}
function display(a,places=6){if(a.n>0n&&cmp(a,R(1n,10n**BigInt(places)))<0)return '< '+format(R(1n,10n**BigInt(places)),places).text;const f=format(a,places);return (f.approx?'≈ ':'')+f.text;}
function listen(ids,fn){ids.forEach(id=>{ $(id).addEventListener('input',fn);$(id).addEventListener('change',fn); });}
const variants=[{id:'V01',color:'Amber',pages:'48',price:8,media:'M01',orderable:true},{id:'V02',color:'Blue',pages:'48',price:9,media:'M02',orderable:false},{id:'V03',color:'Amber',pages:'96',price:12,media:'M03',orderable:true}];
function resolve(color,pages){if(!['Amber','Blue'].includes(color)||!['48','96'].includes(pages))throw Error('Unsupported mock option.');return variants.find(v=>v.color===color&&v.pages===pages)||null;}
function illustration(v){const color=v.color==='Amber'?'#bd8745':'#537e9b',edge=v.pages==='96'?16:8;return '<svg viewBox="0 0 360 320" role="img" aria-label="'+v.media+' schematic placeholder for '+v.color+' '+v.pages+' pages"><path d="M95 40H267V274H95Z" fill="#e4ddd1"/><path d="M88 34H'+(254-edge)+'V268H88Z" fill="'+color+'"/><path d="M104 34V268" stroke="#e9d7b7" stroke-width="2" stroke-dasharray="4 9"/><rect x="127" y="104" width="92" height="72" rx="3" fill="#fcf4e6"/><text x="173" y="135" text-anchor="middle" font-size="18" fill="#42515a">'+v.pages+' pages</text><text x="173" y="158" text-anchor="middle" font-size="13" fill="#68737a">'+v.media+'</text></svg>';}
function update(){const color=$('color').value,pages=$('pages').value,v=resolve(color,pages);put('variantTitle',color+' / '+pages+' pages');if(!v){put('price','—');put('variantId','Selected mock ID: none');$('media').innerHTML='<div class="no-media">No variant-specific media</div>';put('mediaLabel','No matching variant; previous media cleared.');put('availability','No matching variant for '+color+' / '+pages+' pages.');return}put('price','$'+v.price.toFixed(2));put('variantId','Selected mock ID: '+v.id);$('media').innerHTML=illustration(v);put('mediaLabel',v.media+' · original schematic placeholder, not a product photo');put('availability',v.orderable?'Orderable under the supplied mock rule only; actual stock is unverified.':'Not orderable under the supplied mock rule.');}
listen(['color','pages'],update);globalThis.Tool={resolve,update};update();</script></body></html>