Skip to content

Instantly share code, notes, and snippets.

@gingersoulrecords
Last active September 3, 2026 16:08
Show Gist options
  • Select an option

  • Save gingersoulrecords/c99af5cec80eacdc73cf34c317d49b4c to your computer and use it in GitHub Desktop.

Select an option

Save gingersoulrecords/c99af5cec80eacdc73cf34c317d49b4c to your computer and use it in GitHub Desktop.
Big Sky Cart Prototype
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Big Sky — cart drawer prototype</title>
<style>
:root{--s0:#F1EFE8;--s1:#F7F6F2;--s2:#fff;--tp:#2C2C2A;--ts:#5F5E5A;--tm:#888780;--bd:rgba(0,0,0,.12);--bds:rgba(0,0,0,.22);--r:8px}
*{box-sizing:border-box}
body{margin:0;padding:2rem 1rem 3rem;background:var(--s0);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;color:var(--tp);-webkit-font-smoothing:antialiased}
.wrap{max-width:760px;margin:0 auto}
h1{font-size:22px;font-weight:500;margin:0 0 .25rem}
p.lede{font-size:15px;color:var(--ts);line-height:1.6;margin:0 0 1.25rem}
p.sub{font-size:14px;color:var(--ts);line-height:1.6;margin:0 0 1rem}
button{font-family:inherit;background:transparent;border:.5px solid var(--bds);border-radius:var(--r);padding:8px 14px;font-size:14px;color:var(--tp);cursor:pointer}
button:hover{filter:brightness(.97)}
button:active{transform:scale(.98)}
.tabs{display:flex;gap:8px;margin-bottom:1rem}
.tab.on{background:var(--tp);color:#fff;border-color:var(--tp)}
.stage{background:var(--s1);border-radius:12px;padding:1.25rem}
.note{font-size:13px;color:var(--ts);line-height:1.7;margin-top:2rem;padding-top:1.25rem;border-top:.5px solid var(--bd)}
.note strong{font-weight:600}
.hide{display:none}
.track::-webkit-scrollbar{display:none}
@keyframes pulseGlow{0%,100%{box-shadow:0 0 0 0 rgba(255,255,255,0)}50%{box-shadow:0 0 8px 1px rgba(255,255,255,.55)}}
.closeBar{animation:pulseGlow 1.6s ease-in-out infinite}
@keyframes popIn{0%{transform:scale(.6);opacity:0}60%{transform:scale(1.15)}100%{transform:scale(1);opacity:1}}
.pop{animation:popIn .4s ease-out;display:inline-block}
.stepBtn{width:24px;height:24px;padding:0;font-size:14px;line-height:1;flex:none;display:flex;align-items:center;justify-content:center;background:#fff}
</style>
</head>
<body>
<div class="wrap">
<h1>Cart drawer prototype</h1>
<p class="lede">A working sketch of the slide-out cart. Change quantities, add something from Popular picks, and watch what happens as the order crosses $50.</p>
<div class="tabs">
<button class="tab on" id="tMob">Mobile</button>
<button class="tab" id="tDesk">Desktop</button>
<button id="reset" style="margin-left:auto">Reset cart</button>
</div>
<div id="mobView">
<p class="sub">On a phone the cart comes up from the bottom as a sheet, so the shop stays visible behind it. It opens when you tap the cart icon, not automatically on every add.</p>
<div class="stage" style="display:flex;justify-content:center">
<div style="width:340px;background:#F2F0EA;border:.5px solid var(--bd);border-radius:16px;overflow:hidden">
<div class="hdr" style="background:#fff;padding:14px 16px 0;display:flex;align-items:center;justify-content:space-between">
<span style="font-size:17px;font-weight:700">Cart</span><span style="font-size:18px;color:var(--ts);line-height:1">&times;</span>
</div>
<div class="meterWrap" style="background:#fff;padding:16px 16px 16px">
<div class="mMsg" style="text-align:center;line-height:1.35"></div>
</div>
<div style="height:8px;background:#E7E4DC;overflow:hidden"><div class="mBar" style="height:100%;transition:width .45s cubic-bezier(.2,.8,.2,1),background .5s"></div></div>
<div class="items" style="background:#fff;padding:14px 16px 18px"></div>
<div class="recBlock" style="background:#E8E4DA;padding:20px 0 20px;border-top:.5px solid rgba(0,0,0,.1)">
<div style="font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:#8A857A;padding:0 0 12px 16px">Most popular</div>
<div class="track" style="display:flex;gap:10px;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none"></div>
<div style="margin:12px 16px 0;height:3px;border-radius:2px;background:rgba(0,0,0,.12);overflow:hidden"><div class="rail" style="height:100%;background:#4A463D;border-radius:2px;width:40%;transition:width .15s,margin-left .15s"></div></div>
</div>
<div style="padding:16px 14px 14px;background:#F2F0EA">
<div style="background:#fff;border-radius:12px;padding:16px">
<div style="font-size:16px;font-weight:700;margin-bottom:12px">Order summary</div>
<div style="display:flex;justify-content:space-between;font-size:14px;margin-bottom:6px"><span style="color:var(--ts)">Subtotal</span><span class="subTot"></span></div>
<div style="display:flex;justify-content:space-between;align-items:center;font-size:14px;padding-bottom:11px;border-bottom:.5px solid var(--bd)"><span style="color:var(--ts)">Shipping</span><span class="shipTot"></span></div>
<div style="display:flex;justify-content:space-between;align-items:baseline;margin-top:11px"><span style="font-size:14px;font-weight:600">Estimated total</span><span class="estTot" style="font-size:16px;font-weight:700"></span></div>
<div style="font-size:12px;color:var(--ts);margin-top:2px">plus tax, calculated at checkout</div>
<button style="width:100%;font-size:14px;font-weight:700;letter-spacing:.08em;padding:14px;margin-top:14px;background:#111;color:#fff;border-color:#111">CHECKOUT</button>
<div style="font-size:11px;color:var(--ts);line-height:1.5;margin-top:10px;text-align:center" text-wrap:balance">Free shipping over $50. <span style="text-decoration:underline">Some remote areas excluded.</span> Sorry, no PO boxes.</div>
</div>
</div>
</div>
</div>
</div>
<div id="deskView" class="hide">
<p class="sub">On a larger screen it slides in from the right as a full-height panel. The progress bar and totals stay pinned while the items scroll.</p>
<div class="stage">
<div style="position:relative;height:520px;border-radius:8px;overflow:hidden;background:#fff;border:.5px solid var(--bd)">
<div style="position:absolute;inset:0;display:flex;flex-direction:column">
<div style="padding:8px;background:#111;text-align:center;flex:none"><span style="font-size:12px;color:#fff">Free shipping on orders over $50</span></div>
<div style="padding:14px 20px;border-bottom:.5px solid var(--bd);display:flex;align-items:center;justify-content:space-between;flex:none"><span style="font-size:15px;font-weight:700">BIG SKY</span><span style="font-size:13px;color:var(--ts)">Shop &middot; Brands &middot; About</span></div>
<div style="flex:1;padding:20px;display:grid;grid-template-columns:repeat(3,1fr);gap:14px;align-content:start">
<div style="height:100px;border-radius:8px;background:var(--s0)"></div><div style="height:100px;border-radius:8px;background:var(--s0)"></div><div style="height:100px;border-radius:8px;background:var(--s0)"></div>
<div style="height:100px;border-radius:8px;background:var(--s0)"></div><div style="height:100px;border-radius:8px;background:var(--s0)"></div><div style="height:100px;border-radius:8px;background:var(--s0)"></div>
</div>
</div>
<div style="position:absolute;inset:0;background:rgba(0,0,0,.3)"></div>
<div style="position:absolute;top:0;right:0;bottom:0;width:360px;background:#F2F0EA;display:flex;flex-direction:column">
<div class="hdr" style="background:#fff;padding:14px 16px 0;display:flex;align-items:center;justify-content:space-between;flex:none">
<span style="font-size:17px;font-weight:700">Cart</span><span style="font-size:18px;color:var(--ts);line-height:1">&times;</span>
</div>
<div class="meterWrap" style="background:#fff;padding:16px 16px 16px;flex:none">
<div class="mMsg" style="text-align:center;line-height:1.35"></div>
</div>
<div style="height:8px;background:#E7E4DC;overflow:hidden;flex:none"><div class="mBar" style="height:100%;transition:width .45s cubic-bezier(.2,.8,.2,1),background .5s"></div></div>
<div style="flex:1;overflow-y:auto">
<div class="items" style="background:#fff;padding:14px 16px 18px"></div>
<div class="recBlock" style="background:#E8E4DA;padding:20px 0 20px;border-top:.5px solid rgba(0,0,0,.1)">
<div style="font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:#8A857A;padding:0 0 12px 16px">Most popular</div>
<div class="track" style="display:flex;gap:10px;overflow-x:auto;scroll-snap-type:x mandatory;scrollbar-width:none"></div>
<div style="margin:12px 16px 0;height:3px;border-radius:2px;background:rgba(0,0,0,.12);overflow:hidden"><div class="rail" style="height:100%;background:#4A463D;border-radius:2px;width:40%;transition:width .15s,margin-left .15s"></div></div>
</div>
</div>
<div style="padding:14px;background:#F2F0EA;flex:none">
<div style="background:#fff;border-radius:12px;padding:16px">
<div style="display:flex;justify-content:space-between;font-size:14px;margin-bottom:6px"><span style="color:var(--ts)">Subtotal</span><span class="subTot"></span></div>
<div style="display:flex;justify-content:space-between;align-items:center;font-size:14px;padding-bottom:11px;border-bottom:.5px solid var(--bd)"><span style="color:var(--ts)">Shipping</span><span class="shipTot"></span></div>
<div style="display:flex;justify-content:space-between;align-items:baseline;margin-top:11px"><span style="font-size:14px;font-weight:600">Estimated total</span><span class="estTot" style="font-size:16px;font-weight:700"></span></div>
<div style="font-size:12px;color:var(--ts);margin-top:2px">plus tax, calculated at checkout</div>
<button style="width:100%;font-size:14px;font-weight:700;letter-spacing:.08em;padding:13px;margin-top:12px;background:#111;color:#fff;border-color:#111">CHECKOUT</button>
</div>
</div>
</div>
</div>
</div>
</div>
<p class="note">A sketch, not the finished thing. Product photos, type and spacing would match the site. Worth reacting to: <strong>the wording of the nudge</strong>, <strong>the struck-through shipping charge when the order clears $50</strong>, and <strong>whether the Most popular row feels useful or pushy</strong>. The cart opens on tapping the cart icon, never automatically. Taxes and the total are deliberately left as estimates so nothing here can be contradicted at checkout.</p>
</div>
<script>
(function(){
var T=50,S=20,CAP=4,wasFree=false;
var start=[{n:"Cinnamon 6-Pack",p:18,q:1},{n:"Chocolate Mint 6-Pack",p:18,q:1}];
var cart=JSON.parse(JSON.stringify(start));
var best=[{n:"Cotton Candy 6-Pack",p:18},{n:"Rocket Pop 6-Pack",p:18},{n:"Blue Raspberry 6-Pack",p:18},{n:"Peppermint 6-Pack",p:18},{n:"Wintergreen 6-Pack",p:18},{n:"Marshmallow 6-Pack",p:18},{n:"Ginger Delights 12-Pack",p:36},{n:"Citrus Delights 12-Pack",p:36}];
function all(s){return Array.prototype.slice.call(document.querySelectorAll(s))}
function money(){return cart.reduce(function(a,b){return a+b.q*b.p},0)}
function mixSky(t){var n=[11,34,64],d=[46,155,230];
return "rgb("+Math.round(n[0]+(d[0]-n[0])*t)+","+Math.round(n[1]+(d[1]-n[1])*t)+","+Math.round(n[2]+(d[2]-n[2])*t)+")";}
function avail(){var h=cart.map(function(c){return c.n});return best.filter(function(b){return h.indexOf(b.n)<0}).slice(0,CAP)}
function rails(){
all(".track").forEach(function(t){
var r=t.parentNode.querySelector(".rail"); if(!r||!t.scrollWidth) return;
var max=t.scrollWidth-t.clientWidth;
var w=Math.max(18,Math.round(t.clientWidth/t.scrollWidth*100));
r.style.width=w+"%"; r.style.marginLeft=(max>0?(t.scrollLeft/max)*(100-w):0)+"%";
});
}
function draw(){
var sub=money(), free=sub>=T, gap=Math.max(0,T-sub), pct=Math.min(100,Math.round(sub/T*100));
all(".items").forEach(function(box){
box.innerHTML="";
cart.forEach(function(it,i){
var off=it.q<=1;
var r=document.createElement("div");
r.style.cssText="display:flex;align-items:center;gap:12px;padding:12px 0"+(i<cart.length-1?";border-bottom:.5px solid var(--bd)":"");
r.innerHTML='<div style="width:60px;height:45px;border-radius:5px;background:#EDEAE2;flex:none"></div>'+
'<div style="flex:1;min-width:0"><div style="font-size:13px;font-weight:600;line-height:1.3">Big Sky Mints '+it.n+'</div>'+
'<div style="display:flex;align-items:center;gap:8px;margin-top:3px"><span style="font-size:13px;color:var(--ts)">$'+(it.q*it.p).toFixed(2)+'</span>'+
'<button data-rm="'+i+'" style="border:none;background:none;padding:0;font-size:12px;color:var(--ts);text-decoration:underline;cursor:pointer;font-family:inherit">Remove</button></div></div>'+
'<div style="display:flex;align-items:center;gap:6px;flex:none">'+
'<button class="stepBtn" '+(off?'disabled style="opacity:.35;cursor:default"':'')+' data-d="-1" data-i="'+i+'" aria-label="Decrease">&minus;</button>'+
'<span style="font-size:13px;min-width:12px;text-align:center">'+it.q+'</span>'+
'<button class="stepBtn" data-d="1" data-i="'+i+'" aria-label="Increase">+</button></div>';
box.appendChild(r);
});
});
var raw=Math.min(1,sub/T);
all(".mMsg").forEach(function(m){
m.innerHTML = free
? '<div class="'+(wasFree?"":"pop")+'" style="font-size:16px;font-weight:700;color:#1B7FC4">&#127881; Free Shipping unlocked!</div>'+
'<div style="font-size:12px;color:var(--ts);margin-top:3px">$20 saved. Nice one.</div>'
: '<div><span style="font-size:14px;font-weight:500">You&rsquo;re </span>'+
'<span style="font-size:17px;font-weight:700;letter-spacing:-.01em">$'+(gap%1===0?gap.toFixed(0):gap.toFixed(2))+'</span>'+
'<span style="font-size:14px;font-weight:500"> away from </span>'+
'<span style="font-size:14px;font-weight:700">Free Shipping</span></div>'+
'<div style="font-size:12px;color:var(--ts);margin-top:3px">(you&rsquo;ll save $20)</div>';
});
all(".mBar").forEach(function(b){
b.style.width=(free?100:Math.max(9,pct))+"%";
b.style.background = free ? "#2E9BE6" : mixSky(raw);
});
wasFree=free;
var list=avail();
all(".recBlock").forEach(function(blk){
var t=blk.querySelector(".track");
if(!list.length){ blk.style.display="none"; return; }
blk.style.display="block"; t.innerHTML="";
var lead=document.createElement("div"); lead.style.cssText="flex:0 0 6px;width:6px"; t.appendChild(lead);
list.forEach(function(b){
var c=document.createElement("div");
c.style.cssText="flex:none;width:278px;scroll-snap-align:center;background:#fff;border-radius:10px;padding:11px;display:flex;align-items:center;gap:11px";
c.innerHTML='<div style="width:56px;height:42px;border-radius:5px;background:#F2F0EA;flex:none"></div>'+
'<div style="flex:1;min-width:0"><div style="font-size:13px;font-weight:600;line-height:1.3">'+b.n+'</div>'+
'<div style="font-size:13px;color:var(--ts);margin-top:2px">$'+b.p.toFixed(2)+'</div></div>'+
'<button class="addOne" data-n="'+b.n+'" data-p="'+b.p+'" style="font-size:12px;font-weight:700;letter-spacing:.05em;padding:8px 13px;flex:none;background:#F2F0EA;border-color:rgba(0,0,0,.14)">ADD</button>';
t.appendChild(c);
});
var tail=document.createElement("div"); tail.style.cssText="flex:0 0 6px;width:6px"; t.appendChild(tail);
t.onscroll=rails;
});
all(".addOne").forEach(function(b){
b.onclick=function(){ cart.push({n:b.getAttribute("data-n"),p:+b.getAttribute("data-p"),q:1}); draw(); };
});
setTimeout(rails,0);
all(".subTot").forEach(function(e){e.textContent="$"+sub.toFixed(2)});
all(".shipTot").forEach(function(e){
e.innerHTML = free ? '<span style="color:var(--ts);text-decoration:line-through;margin-right:6px">$20.00</span><span style="font-weight:700;color:#1B7FC4">FREE</span>' : '$20.00';
});
all(".estTot").forEach(function(e){e.textContent="$"+(sub+(free?0:S)).toFixed(2)+" CAD"});
all(".items button[data-i]").forEach(function(bt){
bt.onclick=function(){ if(bt.disabled) return;
var i=+bt.getAttribute("data-i"); cart[i].q=Math.max(1,cart[i].q+(+bt.getAttribute("data-d"))); draw(); };
});
all(".items button[data-rm]").forEach(function(bt){
bt.onclick=function(){ cart.splice(+bt.getAttribute("data-rm"),1);
if(!cart.length) cart=[{n:"Cinnamon 6-Pack",p:18,q:1}]; draw(); };
});
}
document.getElementById("tMob").onclick=function(){document.getElementById("mobView").classList.remove("hide");document.getElementById("deskView").classList.add("hide");this.classList.add("on");document.getElementById("tDesk").classList.remove("on");setTimeout(rails,0)};
document.getElementById("tDesk").onclick=function(){document.getElementById("deskView").classList.remove("hide");document.getElementById("mobView").classList.add("hide");this.classList.add("on");document.getElementById("tMob").classList.remove("on");setTimeout(rails,0)};
document.getElementById("reset").onclick=function(){cart=JSON.parse(JSON.stringify(start));wasFree=false;draw()};
draw();
})();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment