← back to Dw Photo Capture
public/kid-simple/v7-scan-and-go.html
325 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>v7 — Scan & Go</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Spectral:wght@500;600;700&family=Manrope:wght@500;600;700;800&display=swap" rel="stylesheet">
<style>
:root{
--teal:#0F3D3E; --teal-2:#164C4D; --copper:#C7784F; --copper-dark:#9B5A38;
--parchment:#F6EFE4; --ink:#2E2A26; --muted:#8A7F6E; --sage:#5C8F6B;
--line:#E7DDCB;
}
*{box-sizing:border-box;}
html,body{margin:0;padding:0;background:#d9d3c8;font-family:'Manrope',sans-serif;color:var(--ink);}
body{display:flex; align-items:flex-start; justify-content:center; min-height:100vh; padding:20px 0;}
.phone{
width:390px; min-height:780px; background:var(--parchment); border-radius:36px; overflow:hidden;
box-shadow:0 24px 60px rgba(0,0,0,.30); display:flex; flex-direction:column; position:relative;
}
.screen{ flex:1; display:flex; flex-direction:column; }
h1,h2{ font-family:'Spectral',serif; margin:0; }
button{ font-family:'Manrope',sans-serif; cursor:pointer; }
/* ---- viewfinder (dark scan screens) ---- */
.viewfinder{
flex:1; min-height:780px; background:radial-gradient(120% 90% at 50% 0%, #1a5152 0%, var(--teal) 55%, #0A2C2D 100%);
display:flex; flex-direction:column; align-items:center; color:#F6EFE4; position:relative; overflow:hidden;
padding:44px 24px 30px;
}
.dw-mark{ position:absolute; top:16px; left:20px; font-size:12px; letter-spacing:2px; color:rgba(246,239,228,.55); font-weight:700; }
.hint-top{
font-family:'Spectral',serif; font-weight:600; font-size:22px; line-height:1.3; text-align:center;
margin:14px 0 22px; color:#FBF7F0;
}
.scan-frame{
width:220px; height:220px; position:relative; margin:6px auto 20px; flex-shrink:0;
}
.scan-frame .corner{ position:absolute; width:36px; height:36px; border-color:var(--copper); border-style:solid; }
.corner.tl{ top:0; left:0; border-width:5px 0 0 5px; border-top-left-radius:10px; }
.corner.tr{ top:0; right:0; border-width:5px 5px 0 0; border-top-right-radius:10px; }
.corner.bl{ bottom:0; left:0; border-width:0 0 5px 5px; border-bottom-left-radius:10px; }
.corner.br{ bottom:0; right:0; border-width:0 5px 5px 0; border-bottom-right-radius:10px; }
.scan-frame .tag-icon{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center; font-size:64px; opacity:.9; }
.scanline{
position:absolute; left:8px; right:8px; height:3px; background:linear-gradient(90deg, transparent, #FFD9B0, transparent);
box-shadow:0 0 12px 2px rgba(255,178,110,.7); animation:sweep 1.8s ease-in-out infinite;
}
@keyframes sweep{ 0%{top:6px;} 50%{top:206px;} 100%{top:6px;} }
.scan-status{ min-height:22px; font-size:15px; font-weight:700; color:#FFD9B0; text-align:center; margin-bottom:14px; }
.toggle-row{ display:flex; gap:10px; width:100%; margin-bottom:18px; }
.toggle-btn{
flex:1; min-height:56px; border-radius:16px; border:2px solid rgba(246,239,228,.28); background:rgba(246,239,228,.08);
color:#F6EFE4; font-size:15px; font-weight:700; display:flex; align-items:center; justify-content:center; gap:6px;
}
.toggle-btn.active{ background:var(--copper); border-color:var(--copper); color:#2A1608; }
.toggle-btn:focus-visible{ outline:3px solid #6fd3d6; outline-offset:2px; }
.scanbtn{
width:100%; min-height:68px; border-radius:22px; background:var(--copper); border:none; color:#2A1608;
font-size:20px; font-weight:800; letter-spacing:.3px; box-shadow:0 10px 24px rgba(199,120,79,.35);
}
.scanbtn:disabled{ opacity:.7; }
.scanbtn:focus-visible{ outline:3px solid #6fd3d6; outline-offset:2px; }
.vf-link{
margin-top:16px; background:none; border:none; color:#D9CFC0; font-size:16px; text-decoration:underline;
min-height:56px; width:100%;
}
.vf-link:focus-visible{ outline:3px solid #6fd3d6; outline-offset:2px; }
.shutter-wrap{ flex:1; display:flex; align-items:flex-end; justify-content:center; padding-bottom:26px; width:100%; }
.shutter{
width:92px; height:92px; border-radius:50%; background:var(--copper); border:6px solid #F6EFE4;
font-size:36px; display:flex; align-items:center; justify-content:center; box-shadow:0 10px 26px rgba(0,0,0,.35);
}
.shutter:focus-visible{ outline:3px solid #6fd3d6; outline-offset:2px; }
/* ---- card / content screens (warm parchment) ---- */
.card-screen{ flex:1; display:flex; flex-direction:column; padding:34px 26px 28px; background:var(--parchment); }
.topbar{ display:flex; align-items:center; justify-content:space-between; margin-bottom:6px; }
.homefab{
min-width:52px; min-height:52px; border-radius:14px; background:#fff; border:2px solid var(--line);
color:var(--ink); font-size:14px; font-weight:700; display:flex; align-items:center; justify-content:center; gap:4px;
padding:0 12px;
}
.homefab:focus-visible{ outline:3px solid #2B6CB0; outline-offset:2px; }
.kicker{ font-size:13px; font-weight:800; letter-spacing:1.5px; text-transform:uppercase; color:var(--copper-dark); margin:2px 0 8px; }
.headline{ font-size:28px; font-weight:600; line-height:1.22; color:var(--ink); margin:0 0 22px; }
.item-card{
background:#fff; border:2px solid var(--line); border-radius:22px; padding:20px; display:flex; flex-direction:column;
align-items:center; gap:10px; margin-bottom:22px;
}
.swatch{ width:100%; height:150px; border-radius:16px; }
.sw1{background:repeating-linear-gradient(45deg,#5C8F6B 0 18px,#4c7a5a 18px 36px);}
.sw2{background:repeating-linear-gradient(-45deg,#7C93B0 0 16px,#617a99 16px 32px);}
.sw3{background:repeating-linear-gradient(90deg,#C7784F 0 14px,#9B5A38 14px 28px);}
.itemname{ font-size:20px; font-weight:800; margin:0; text-align:center; }
.itemmeta{ font-size:15px; color:var(--muted); margin:0; text-align:center; }
.stack{ display:flex; flex-direction:column; gap:14px; }
.btn{
width:100%; min-height:60px; border-radius:18px; font-size:18px; font-weight:800; padding:10px 16px;
border:2px solid var(--line); background:#fff; color:var(--ink);
}
.btn-primary{ background:var(--copper); border-color:var(--copper); color:#2A1608; }
.btn-secondary{ background:var(--teal); border-color:var(--teal); color:#F6EFE4; }
.btn:focus-visible{ outline:3px solid #2B6CB0; outline-offset:2px; }
.link{
background:none; border:none; color:var(--muted); font-size:16px; text-decoration:underline;
min-height:56px; width:100%; margin-top:6px;
}
.link:focus-visible{ outline:3px solid #2B6CB0; outline-offset:2px; }
.suggest-row{
display:flex; align-items:center; gap:14px; background:#fff; border:2px solid var(--line); border-radius:18px;
padding:12px 14px; min-height:64px; width:100%; text-align:left; margin-bottom:12px;
}
.suggest-row .sw-mini{ width:52px; height:52px; border-radius:12px; flex-shrink:0; }
.suggest-row .txt{ display:flex; flex-direction:column; gap:2px; }
.suggest-row .txt b{ font-size:16px; }
.suggest-row .txt span{ font-size:13px; color:var(--muted); }
.brandlist{ display:flex; flex-direction:column; gap:12px; overflow-y:auto; }
.brand-row{
width:100%; min-height:60px; border-radius:16px; border:2px solid var(--line); background:#fff; color:var(--ink);
font-size:17px; font-weight:700; text-align:left; padding:0 18px;
}
.center{ display:flex; flex-direction:column; align-items:center; justify-content:center; flex:1; gap:14px; text-align:center; }
.bigcheck{ font-size:56px; }
</style>
</head>
<body>
<div class="phone">
<div class="screen" id="screen" role="main"></div>
</div>
<script>
const MOCK_ITEMS = [
{name:'Palm Damask', color:'Sage', code:'DW-10423', sw:'sw1'},
{name:'Trellis Weave', color:'Indigo', code:'DW-88214', sw:'sw2'},
{name:'Linen Bloom', color:'Blush', code:'DW-55071', sw:'sw3'},
];
const BRANDS = ['York Wallcoverings','Brewster Home','Kravet','Cole & Son','Thibaut','Ronald Redding'];
const state = { screen:'home', simMode:'known', item:null };
function go(screen){ state.screen = screen; render(); }
function randomItem(){ return MOCK_ITEMS[Math.floor(Math.random()*MOCK_ITEMS.length)]; }
function homeFab(){ return `<div class="topbar"><button class="homefab" onclick="go('home')">⌂ Home</button><span></span></div>`; }
function doScan(){
const btn = document.getElementById('scanBtn');
const st = document.getElementById('scanStatus');
if(btn) btn.disabled = true;
if(st) st.textContent = 'Scanning…';
setTimeout(()=>{
if(state.simMode==='known'){ state.item = randomItem(); go('scan-match'); }
else { go('scan-nomatch'); }
}, 850);
}
function render(){
const s = document.getElementById('screen');
let html = '';
if(state.screen==='home'){
html += `<div class="viewfinder">
<div class="dw-mark">DESIGNER WALLCOVERINGS</div>
<div class="hint-top">Point your camera<br>at a tag or box</div>
<div class="scan-frame">
<div class="corner tl"></div><div class="corner tr"></div>
<div class="corner bl"></div><div class="corner br"></div>
<div class="tag-icon">${state.simMode==='known' ? '🏷️' : '🆕'}</div>
<div class="scanline"></div>
</div>
<div class="scan-status" id="scanStatus"> </div>
<div class="toggle-row">
<button class="toggle-btn ${state.simMode==='known'?'active':''}" onclick="state.simMode='known'; render();">🏷️ Known Tag</button>
<button class="toggle-btn ${state.simMode!=='known'?'active':''}" onclick="state.simMode='new'; render();">🆕 New Item</button>
</div>
<button class="scanbtn" id="scanBtn" onclick="doScan()">Tap to Scan</button>
<button class="vf-link" onclick="go('find-search')">Or type a code instead</button>
</div>`;
}
else if(state.screen==='scan-match'){
html += `<div class="card-screen">${homeFab()}
<div class="kicker">Found It</div>
<h1 class="headline">What do you want to do?</h1>
<div class="item-card">
<div class="swatch ${state.item.sw}"></div>
<p class="itemname">${state.item.name} — ${state.item.color}</p>
<p class="itemmeta">code ${state.item.code}</p>
</div>
<div class="stack">
<button class="btn btn-primary" onclick="go('update-camera')">Update It</button>
<button class="btn btn-secondary" onclick="go('sample-printing')">Sample Just Arrived</button>
<button class="link" onclick="go('home')">Not this one — scan again</button>
</div>
</div>`;
}
else if(state.screen==='scan-nomatch'){
html += `<div class="card-screen">${homeFab()}
<div class="center" style="flex:none; margin:30px 0 26px;">
<div class="bigcheck">🔎</div>
<h1 class="headline" style="margin-bottom:0;">Hmm, we don't<br>recognize this one.</h1>
</div>
<div class="stack">
<button class="btn btn-primary" onclick="go('add-brand')">Add It as New</button>
<button class="link" onclick="go('home')">Scan Again</button>
</div>
</div>`;
}
else if(state.screen==='add-camera'){
html += cameraScreen('Take a photo of it.', "add-brand");
}
else if(state.screen==='add-brand'){
html += `<div class="card-screen">${homeFab()}
<div class="kicker">Almost Done</div>
<h1 class="headline">Which brand is it?</h1>
<div class="brandlist">${BRANDS.map(b=>`<button class="brand-row" onclick="go('add-done')">${b}</button>`).join('')}</div>
</div>`;
}
else if(state.screen==='add-done'){
html += `<div class="card-screen"><div class="center">
<div class="bigcheck">🎉</div>
<h1 class="headline" style="margin-bottom:0;">Saved as a new item!</h1>
<div class="stack" style="width:100%; margin-top:18px;">
<button class="btn btn-primary" onclick="go('home')">Scan Something Else</button>
</div>
</div></div>`;
}
else if(state.screen==='update-camera'){
html += cameraScreen('Take the new photo.', "update-done");
}
else if(state.screen==='update-done'){
html += `<div class="card-screen"><div class="center">
<div class="bigcheck">✅</div>
<h1 class="headline" style="margin-bottom:0;">Updated!</h1>
<div class="stack" style="width:100%; margin-top:18px;">
<button class="btn btn-primary" onclick="go('home')">Scan Something Else</button>
</div>
</div></div>`;
}
else if(state.screen==='sample-printing'){
html += `<div class="card-screen"><div class="center">
<div class="bigcheck">🖨️</div>
<h1 class="headline" style="margin-bottom:0;">Printing your sticker…</h1>
</div></div>`;
setTimeout(()=>{ if(state.screen==='sample-printing') go('sample-done'); }, 1100);
}
else if(state.screen==='sample-done'){
html += `<div class="card-screen"><div class="center">
<div class="bigcheck">✅</div>
<h1 class="headline" style="margin-bottom:0;">Done! Sample marked received.</h1>
<div class="stack" style="width:100%; margin-top:18px;">
<button class="btn btn-primary" onclick="go('home')">Scan Something Else</button>
</div>
</div></div>`;
}
else if(state.screen==='find-search'){
html += `<div class="card-screen">${homeFab()}
<div class="kicker">Find An Item</div>
<h1 class="headline">Which one are you<br>looking for?</h1>
${MOCK_ITEMS.map(it=>`
<button class="suggest-row" onclick='findPick("${it.code}")'>
<div class="sw-mini ${it.sw}"></div>
<div class="txt"><b>${it.name}</b><span>${it.color} · code ${it.code}</span></div>
</button>`).join('')}
<button class="vf-link" style="color:var(--muted); margin-top:4px;" onclick="go('home')">Scan instead</button>
</div>`;
}
else if(state.screen==='find-detail'){
html += `<div class="card-screen">${homeFab()}
<div class="kicker">Here It Is</div>
<div class="item-card">
<div class="swatch ${state.item.sw}"></div>
<p class="itemname">${state.item.name} — ${state.item.color}</p>
<p class="itemmeta">code ${state.item.code}</p>
</div>
<div class="stack">
<button class="btn btn-primary" onclick="go('update-camera')">Update This</button>
<button class="btn btn-secondary" onclick="go('find-search')">Search Again</button>
</div>
</div>`;
}
s.innerHTML = html;
}
function cameraScreen(hint, nextScreen){
return `<div class="viewfinder" style="padding-top:56px;">
<div class="dw-mark">DESIGNER WALLCOVERINGS</div>
<div class="hint-top">${hint}</div>
<div class="shutter-wrap">
<button class="shutter" aria-label="Take photo" onclick="shoot('${nextScreen}')">📸</button>
</div>
</div>`;
}
function shoot(nextScreen){
const btn = document.querySelector('.shutter');
if(btn){ btn.disabled = true; btn.style.opacity = .6; }
setTimeout(()=> go(nextScreen), 650);
}
function findPick(code){
state.item = MOCK_ITEMS.find(i=>i.code===code);
go('find-detail');
}
render();
</script>
</body>
</html>