@liveloop · 5/17/2026, 11:35:54 AM
Initial version — all lines are new.
+<style>+ html,body{height:100%;margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;background:#0b1020;color:#f8fafc;overflow:hidden;}+ .wrap{display:flex;flex-direction:column;height:100%;box-sizing:border-box;padding:.85rem;gap:.55rem;}+ .top{display:flex;align-items:baseline;justify-content:space-between;gap:.5rem;}+ h1{margin:0;font-size:1rem;}+ #count{font-size:.78rem;color:#94a3b8;font-variant-numeric:tabular-nums;}+ .stage{flex:1;display:flex;gap:.6rem;min-height:0;}+ #photo{flex:1;border-radius:1rem;background:#1e293b;background-size:cover;background-position:center;display:flex;flex-direction:column;align-items:center;justify-content:center;overflow:hidden;box-shadow:0 10px 30px rgba(0,0,0,.45);}+ #emoji{font-size:min(30vw,130px);line-height:1;filter:drop-shadow(0 6px 14px rgba(0,0,0,.35));}+ #caption{margin-top:.35rem;font-weight:700;font-size:1.05rem;color:#fff;text-shadow:0 2px 10px rgba(0,0,0,.55);}+ .slider{width:46px;display:flex;align-items:stretch;justify-content:center;padding:.5rem 0;touch-action:none;user-select:none;-webkit-user-select:none;cursor:pointer;}+ #track{position:relative;width:8px;background:#334155;border-radius:9999px;}+ #fillbar{position:absolute;left:0;right:0;top:0;background:linear-gradient(#60a5fa,#3b82f6);border-radius:9999px;}+ #handle{position:absolute;left:50%;top:0;width:34px;height:34px;border-radius:50%;background:#fff;color:#1e293b;box-shadow:0 3px 12px rgba(0,0,0,.55);transform:translate(-50%,-50%);display:flex;align-items:center;justify-content:center;font-size:1rem;}+ .hint{font-size:.7rem;color:#64748b;text-align:center;}+ .addrow{display:flex;justify-content:center;}+ .add{font:inherit;font-size:.82rem;font-weight:600;border:0;border-radius:9999px;padding:.55rem 1.1rem;background:#3b82f6;color:#fff;cursor:pointer;touch-action:manipulation;display:inline-flex;align-items:center;gap:.35rem;}+ .hidden{display:none;}+</style>+<div class="wrap">+ <div class="top"><h1>📷 Photo Slider</h1><span id="count">1 / 3</span></div>+ <div class="stage">+ <div id="photo"><div id="emoji">🌅</div><div id="caption">Sunrise</div></div>+ <div class="slider" id="slider">+ <div id="track"><div id="fillbar"></div><div id="handle">⋮</div></div>+ </div>+ </div>+ <div class="hint">Drag the bar down for the next photo, up for the previous</div>+ <div class="addrow">+ <label class="add">➕ Add photo<input type="file" accept="image/*" multiple id="pick" class="hidden"></label>+ </div>+</div>+<script>+(function(){+ // Remixing? Put image URLs in START_PHOTOS to replace the examples.+ var START_PHOTOS = [];++ var slides = START_PHOTOS.length+ ? START_PHOTOS.map(function(u){ return { url:u }; })+ : [+ { grad:'linear-gradient(160deg,#fde68a,#fb923c,#ef4444)', emoji:'🌅', label:'Sunrise' },+ { grad:'linear-gradient(160deg,#a5f3fc,#0ea5e9,#1e3a8a)', emoji:'🌊', label:'Ocean' },+ { grad:'linear-gradient(160deg,#bbf7d0,#22c55e,#14532d)', emoji:'🌲', label:'Forest' }+ ];+ var idx=0;+ var photo=document.getElementById('photo'),emoji=document.getElementById('emoji'),+ caption=document.getElementById('caption'),count=document.getElementById('count'),+ slider=document.getElementById('slider'),track=document.getElementById('track'),+ handle=document.getElementById('handle'),fillbar=document.getElementById('fillbar'),+ pick=document.getElementById('pick');++ function render(){+ var s=slides[idx];+ if(s.url){+ photo.style.backgroundImage='url("'+s.url+'")';+ emoji.style.display='none'; caption.style.display='none';+ } else {+ photo.style.backgroundImage=s.grad;+ emoji.style.display=''; caption.style.display='';+ emoji.textContent=s.emoji; caption.textContent=s.label;+ }+ count.textContent=(idx+1)+' / '+slides.length;+ var pct=slides.length<2?0:idx/(slides.length-1);+ handle.style.top=(pct*100)+'%';+ fillbar.style.height=(pct*100)+'%';+ }+ function setIdx(n){+ n=Math.max(0,Math.min(slides.length-1,n));+ if(n!==idx) idx=n;+ render();+ }+ function posToIdx(clientY){+ var r=track.getBoundingClientRect();+ if(r.height<=0) return idx;+ var pct=(clientY-r.top)/r.height;+ pct=Math.max(0,Math.min(1,pct));+ return Math.round(pct*(slides.length-1));+ }++ var dragging=false;+ slider.addEventListener('pointerdown',function(e){+ dragging=true;+ try{ slider.setPointerCapture(e.pointerId); }catch(_){}+ setIdx(posToIdx(e.clientY));+ e.preventDefault();+ });+ slider.addEventListener('pointermove',function(e){+ if(dragging){ setIdx(posToIdx(e.clientY)); e.preventDefault(); }+ });+ function endDrag(e){+ dragging=false;+ try{ slider.releasePointerCapture(e.pointerId); }catch(_){}+ }+ slider.addEventListener('pointerup',endDrag);+ slider.addEventListener('pointercancel',endDrag);++ pick.addEventListener('change',function(e){+ var files=e.target.files; if(!files||!files.length) return;+ var pending=files.length;+ function done(){ if(--pending===0) setIdx(slides.length-1); }+ Array.prototype.forEach.call(files,function(file){+ var reader=new FileReader();+ reader.onload=function(){+ var img=new Image();+ img.onload=function(){+ try{+ var max=900,sc=Math.min(1,max/Math.max(img.width,img.height));+ var c=document.createElement('canvas');+ c.width=Math.round(img.width*sc); c.height=Math.round(img.height*sc);+ c.getContext('2d').drawImage(img,0,0,c.width,c.height);+ slides.push({ url:c.toDataURL('image/jpeg',0.85) });+ }catch(err){ slides.push({ url:reader.result }); }+ done();+ };+ img.onerror=function(){ slides.push({ url:reader.result }); done(); };+ img.src=reader.result;+ };+ reader.onerror=done;+ reader.readAsDataURL(file);+ });+ });++ render();+})();+</script>