[object Object]

← back to Dw Photo Capture

Onload chooser: Adobe Spectrum themes (Light/Dark/Darkest/Auto) + accents, smart Suggested action, full UX (TK-12346)

6e2c3f26066caa4738bb3ee9bb3386d8f3b82eca · 2026-09-26 10:42:20 -0700 · Steve Abrams

Themed card chooser with icons, descriptions, 1-4/Enter shortcuts, incoming-samples
badge, last-used fallback suggestion, ARIA radiogroups, reduced-motion. Tokens scoped
to #homeScreen; theme/accent persisted in localStorage.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BnDgaJLtEUJykffiozVpQF

Files touched

Diff

commit 6e2c3f26066caa4738bb3ee9bb3386d8f3b82eca
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Sep 26 10:42:20 2026 -0700

    Onload chooser: Adobe Spectrum themes (Light/Dark/Darkest/Auto) + accents, smart Suggested action, full UX (TK-12346)
    
    Themed card chooser with icons, descriptions, 1-4/Enter shortcuts, incoming-samples
    badge, last-used fallback suggestion, ARIA radiogroups, reduced-motion. Tokens scoped
    to #homeScreen; theme/accent persisted in localStorage.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01BnDgaJLtEUJykffiozVpQF
---
 public/index.html | 180 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 158 insertions(+), 22 deletions(-)

diff --git a/public/index.html b/public/index.html
index ce8a83a..9435d0a 100644
--- a/public/index.html
+++ b/public/index.html
@@ -419,18 +419,69 @@
   #mUse{background:#2bd06a;color:#032}#mClose{background:#222;color:#fff}
   .add-spec{display:flex;justify-content:space-between;gap:12px;font-size:12.5px;padding:4px 0;border-bottom:1px solid rgba(255,255,255,.05)}
   .add-spec span{color:#9a917d}.add-spec b{color:var(--ink);text-align:right}
-  /* onload chooser — three white pills on black */
-  #homeScreen{position:fixed;inset:0;z-index:200;background:#000;display:flex;align-items:center;justify-content:center}
+  /* ── onload chooser (TK-12346): Adobe Spectrum themes (Light · Dark · Darkest) + accent, full UX ──
+     Tokens are scoped to #homeScreen so the rest of the app is untouched. Theme/accent persist in localStorage. */
+  #homeScreen{--h-bg:#080808;--h-surface:#1a1a1a;--h-surface-2:#232323;--h-border:#2c2c2c;--h-border-hi:#4a4a4a;
+    --h-ink:#e8e8e8;--h-muted:#9a9a9a;--h-accent:#2680eb;--h-accent-ink:#fff;--h-accent-soft:rgba(38,128,235,.16);--h-focus:#5aa9fa;
+    position:fixed;inset:0;z-index:200;background:var(--h-bg);color:var(--h-ink);overflow-y:auto;-webkit-overflow-scrolling:touch;
+    font-family:"Adobe Clean","Source Sans 3","SF Pro Text",-apple-system,system-ui,sans-serif;transition:background .2s,color .2s}
   #homeScreen[hidden]{display:none}
-  .home-inner{display:flex;flex-direction:column;gap:16px;align-items:center;width:100%;max-width:340px;padding:24px}
-  .home-title{color:#fff;font-size:14px;letter-spacing:.1em;text-transform:uppercase;opacity:.55;margin-bottom:16px}
-  .home-pill{width:100%;background:#fff;color:#000;border:none;border-radius:999px;padding:20px;font-size:19px;font-weight:700;cursor:pointer}
-  .home-pill:active{transform:scale(.98)}
-  .home-skip{margin-top:10px;background:none;border:none;color:#fff;opacity:.4;font-size:14px;cursor:pointer}
-  .home-apps{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;width:100%;margin-top:20px;padding-top:16px;border-top:1px solid rgba(255,255,255,.15)}
-  .home-apps-lbl{width:100%;text-align:center;color:#fff;opacity:.45;font-size:11px;letter-spacing:.1em;text-transform:uppercase}
-  .home-apps a{color:#fff;text-decoration:none;border:1px solid rgba(255,255,255,.3);border-radius:999px;padding:8px 13px;font-size:13px}
+  #homeScreen[data-theme=dark]{--h-bg:#1d1d1d;--h-surface:#2c2c2c;--h-surface-2:#353535;--h-border:#3e3e3e;--h-border-hi:#5a5a5a;--h-ink:#ebebeb;--h-muted:#a8a8a8}
+  #homeScreen[data-theme=light]{--h-bg:#f5f5f5;--h-surface:#ffffff;--h-surface-2:#f0f0f0;--h-border:#dadada;--h-border-hi:#b1b1b1;--h-ink:#1f1f1f;--h-muted:#6d6d6d;--h-focus:#1473e6}
+  #homeScreen[data-accent=gold]{--h-accent:#c9a45c;--h-accent-ink:#1b1407;--h-accent-soft:rgba(201,164,92,.18)}
+  #homeScreen[data-accent=green]{--h-accent:#12805c;--h-accent-ink:#fff;--h-accent-soft:rgba(18,128,92,.18)}
+  #homeScreen[data-accent=purple]{--h-accent:#7e4bf3;--h-accent-ink:#fff;--h-accent-soft:rgba(126,75,243,.18)}
+  #homeScreen[data-accent=red]{--h-accent:#d31510;--h-accent-ink:#fff;--h-accent-soft:rgba(211,21,16,.15)}
+  .home-inner{display:flex;flex-direction:column;gap:12px;width:100%;max-width:440px;margin:0 auto;
+    padding:max(18px,env(safe-area-inset-top)) 16px max(22px,env(safe-area-inset-bottom));min-height:100%;box-sizing:border-box;justify-content:center}
+  .home-top{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-bottom:4px}
+  .home-title{font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--h-muted);font-weight:700}
+  .home-greet{font-size:22px;font-weight:800;line-height:1.15;margin-top:4px;color:var(--h-ink)}
+  .home-date{font-size:12.5px;color:var(--h-muted);margin-top:2px}
+  .home-theme-btn{flex:0 0 auto;display:flex;align-items:center;gap:6px;background:var(--h-surface);color:var(--h-ink);border:1px solid var(--h-border);
+    border-radius:999px;padding:7px 11px;font-weight:600;font-size:12.5px;line-height:1;font-family:inherit;cursor:pointer}
+  .home-theme-btn .dot{width:12px;height:12px;border-radius:50%;background:var(--h-accent)}
+  .home-prefs{background:var(--h-surface);border:1px solid var(--h-border);border-radius:14px;padding:12px;display:flex;flex-direction:column;gap:10px}
+  .home-prefs[hidden]{display:none}
+  .home-prefs-lbl{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--h-muted);font-weight:700}
+  .seg{display:flex;background:var(--h-surface-2);border:1px solid var(--h-border);border-radius:10px;padding:3px;gap:3px}
+  .seg button{flex:1;background:none;border:none;border-radius:7px;padding:9px 4px;color:var(--h-muted);font-weight:600;font-size:13px;line-height:1;font-family:inherit;cursor:pointer}
+  .seg button[aria-checked=true]{background:var(--h-surface);color:var(--h-ink);box-shadow:0 1px 2px rgba(0,0,0,.25),inset 0 0 0 1px var(--h-border-hi)}
+  .swatches{display:flex;gap:10px}
+  .swatches button{width:30px;height:30px;border-radius:50%;border:2px solid transparent;cursor:pointer;padding:0;box-shadow:inset 0 0 0 2px var(--h-surface)}
+  .swatches button[aria-checked=true]{border-color:var(--h-ink)}
+  .home-sug{font-size:13px;color:var(--h-muted);min-height:18px;line-height:1.35}
+  .home-sug b{color:var(--h-ink)}
+  .home-card{position:relative;display:grid;grid-template-columns:44px 1fr auto;align-items:center;gap:12px;width:100%;text-align:left;
+    background:var(--h-surface);color:var(--h-ink);border:1px solid var(--h-border);border-radius:16px;padding:14px 14px;cursor:pointer;
+    font:inherit;transition:transform .12s,border-color .15s,background .15s,box-shadow .15s}
+  .home-card:hover{border-color:var(--h-border-hi)}
+  .home-card:active{transform:scale(.985)}
+  .home-card .ic{width:44px;height:44px;border-radius:12px;display:grid;place-items:center;background:var(--h-surface-2);color:var(--h-accent)}
+  .home-card .ic svg{width:24px;height:24px}
+  .home-card .tt{display:block;font-size:17px;font-weight:800;line-height:1.2}
+  .home-card .ds{display:block;font-size:12.5px;color:var(--h-muted);margin-top:3px;line-height:1.3}
+  .home-card kbd{font:600 11px/1 ui-monospace,Menlo,monospace;color:var(--h-muted);border:1px solid var(--h-border);border-bottom-width:2px;border-radius:5px;padding:4px 6px}
+  .home-card .badge{position:absolute;top:-7px;right:12px;background:var(--h-accent);color:var(--h-accent-ink);font-weight:800;font-size:11px;line-height:1;font-family:inherit;border-radius:999px;padding:4px 8px}
+  .home-card .badge[hidden]{display:none}
+  .home-card.suggested{border-color:var(--h-accent);background:linear-gradient(0deg,var(--h-accent-soft),var(--h-accent-soft)),var(--h-surface);box-shadow:0 0 0 1px var(--h-accent)}
+  .home-card .home-chip{display:none;width:max-content;margin-bottom:4px;font-weight:800;font-size:10px;line-height:1;letter-spacing:.08em;text-transform:uppercase;
+    color:var(--h-accent);border:1px solid var(--h-accent);border-radius:999px;padding:3px 7px}
+  .home-card.suggested .home-chip{display:block}
+  .home-card.suggested .ic{background:var(--h-accent);color:var(--h-accent-ink)}
+  #homeScreen button:focus-visible,#homeScreen a:focus-visible{outline:2px solid var(--h-accent);outline-offset:2px}
+  .home-skip{align-self:center;margin-top:2px;background:none;border:none;color:var(--h-muted);font-weight:600;font-size:14px;line-height:1;font-family:inherit;padding:10px;cursor:pointer}
+  .home-apps{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;width:100%;margin-top:6px;padding-top:14px;border-top:1px solid var(--h-border)}
+  .home-apps-lbl{width:100%;text-align:center;color:var(--h-muted);font-size:11px;letter-spacing:.1em;text-transform:uppercase;font-weight:700}
+  .home-apps a{color:var(--h-ink);text-decoration:none;background:var(--h-surface);border:1px solid var(--h-border);border-radius:999px;padding:8px 13px;font-size:13px}
   .home-apps a:active{transform:scale(.98)}
+  .home-hint{text-align:center;font-size:11px;color:var(--h-muted);opacity:.8}
+  @media (hover:none){ .home-card kbd,.home-hint{display:none} }
+  @media (prefers-reduced-motion:no-preference){
+    #homeScreen:not([hidden]) .home-card{animation:homeIn .28s both}
+    #homeScreen .home-card[data-act=update]{animation-delay:.04s}#homeScreen .home-card[data-act=lookup]{animation-delay:.08s}#homeScreen .home-card[data-act=check]{animation-delay:.12s}
+  }
+  @keyframes homeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
   #homeBtn{background:none;border:none;color:var(--ink);opacity:.5;font-size:18px;cursor:pointer;float:right}
   .ed-panel{flex:0 0 auto;background:var(--card);border-top:1px solid var(--line);padding:12px 14px max(14px,env(safe-area-inset-bottom));max-height:54vh;overflow-y:auto}
   .ed-head{display:flex;align-items:center;gap:10px;margin-bottom:10px}
@@ -487,15 +538,53 @@
 </style>
 </head>
 <body>
-<!-- onload chooser: three simple pills, white on black, centered -->
-<div id="homeScreen">
+<!-- onload chooser (TK-12346): Adobe Spectrum themes + accent, smart "Suggested" action, keyboard 1–4 / Enter -->
+<div id="homeScreen" role="dialog" aria-modal="true" aria-labelledby="homeGreet" data-theme="darkest" data-accent="blue">
   <div class="home-inner">
-    <div class="home-title">DW Sample Station</div>
-    <button class="home-pill" data-act="add">Add New SKU</button>
-    <button class="home-pill" data-act="update">Update SKU</button>
-    <button class="home-pill" data-act="lookup">Look Up SKU</button>
-    <button class="home-pill" data-act="check">Check Sample In</button>
-    <button class="home-skip" data-act="skip">skip →</button>
+    <div class="home-top">
+      <div>
+        <div class="home-title">DW Sample Station</div>
+        <div class="home-greet" id="homeGreet">Welcome</div>
+        <div class="home-date" id="homeDate"></div>
+      </div>
+      <button class="home-theme-btn" id="homeThemeBtn" aria-expanded="false" aria-controls="homePrefs"><span class="dot"></span>Theme</button>
+    </div>
+    <div class="home-prefs" id="homePrefs" hidden>
+      <div class="home-prefs-lbl" id="lblTheme">Theme</div>
+      <div class="seg" role="radiogroup" aria-labelledby="lblTheme" id="homeThemeSeg">
+        <button role="radio" data-theme="light">Light</button>
+        <button role="radio" data-theme="dark">Dark</button>
+        <button role="radio" data-theme="darkest">Darkest</button>
+        <button role="radio" data-theme="auto">Auto</button>
+      </div>
+      <div class="home-prefs-lbl" id="lblAccent">Accent</div>
+      <div class="swatches" role="radiogroup" aria-labelledby="lblAccent" id="homeAccent">
+        <button role="radio" data-accent="blue" aria-label="Blue" style="background:#2680eb"></button>
+        <button role="radio" data-accent="gold" aria-label="DW Gold" style="background:#c9a45c"></button>
+        <button role="radio" data-accent="green" aria-label="Green" style="background:#12805c"></button>
+        <button role="radio" data-accent="purple" aria-label="Purple" style="background:#7e4bf3"></button>
+        <button role="radio" data-accent="red" aria-label="Red" style="background:#d31510"></button>
+      </div>
+    </div>
+    <div class="home-sug" id="homeSug" aria-live="polite"></div>
+    <button class="home-card" data-act="add" aria-keyshortcuts="1">
+      <span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="3"/><path d="M12 8v8M8 12h8"/></svg></span><span><span class="home-chip">Suggested</span><span class="tt">Add New SKU</span><span class="ds">Photograph a new sample front + back and create it</span></span><kbd>1</kbd>
+      <span class="badge" hidden></span>
+    </button>
+    <button class="home-card" data-act="update" aria-keyshortcuts="2">
+      <span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12a9 9 0 1 1-2.64-6.36"/><path d="M21 3v6h-6"/></svg></span><span><span class="home-chip">Suggested</span><span class="tt">Update SKU</span><span class="ds">Snap the label, then refresh photos or details</span></span><kbd>2</kbd>
+      <span class="badge" hidden></span>
+    </button>
+    <button class="home-card" data-act="lookup" aria-keyshortcuts="3">
+      <span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg></span><span><span class="home-chip">Suggested</span><span class="tt">Look Up SKU</span><span class="ds">Scan a sample to identify it in the catalog</span></span><kbd>3</kbd>
+      <span class="badge" hidden></span>
+    </button>
+    <button class="home-card" data-act="check" aria-keyshortcuts="4">
+      <span class="ic"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 8 12 3 3 8v8l9 5 9-5z"/><path d="m8 12 3 3 5-6"/></svg></span><span><span class="home-chip">Suggested</span><span class="tt">Check Sample In</span><span class="ds">Mark ordered samples as received</span></span><kbd>4</kbd>
+      <span class="badge" hidden></span>
+    </button>
+    <button class="home-skip" data-act="skip">Browse catalog grid →</button>
+    <div class="home-hint">Press 1–4 to choose · Enter for the suggestion</div>
     <!-- Other apps (TK-12162): the 4 simplified capture concepts -->
     <nav class="home-apps" aria-label="Other apps">
       <div class="home-apps-lbl">Other apps</div>
@@ -2933,8 +3022,11 @@ if(!(window.SpeechRecognition||window.webkitSpeechRecognition)){
   console.log('Web Speech API unavailable — mic buttons hidden (native keyboard mic still works).');
 }
 
-// ── onload chooser: Add New SKU · Update SKU · Check Sample In ──
-function routeHome(act){ $('#homeScreen').hidden=true;
+// ── onload chooser (TK-12346): Add · Update · Look Up · Check In — themed (Adobe Spectrum) + smart suggestion ──
+const HOME_KEYS={'1':'add','2':'update','3':'lookup','4':'check'};
+function hGet(k,d){ try{ return localStorage.getItem(k)||d; }catch(e){ return d; } }
+function hSet(k,v){ try{ localStorage.setItem(k,v); }catch(e){} }
+function routeHome(act){ $('#homeScreen').hidden=true; if(act!=='skip') hSet('dwHomeLast',act);
   // onload → straight to the camera; details show after the first scan
   if(act==='add') openAddModal(null,{mode:'add',camera:true});
   else if(act==='update') openAddModal(null,{mode:'update',camera:true});
@@ -2942,8 +3034,52 @@ function routeHome(act){ $('#homeScreen').hidden=true;
   else if(act==='check'){ openFbModal(); loadIncoming(); }   // show the incoming (ordered-not-received) queue first
   else if(act==='skip'){ if(!TWILITEMS.length) loadTwil(); }   // lazy-load the big TWIL grid only when actually viewing it
 }
-document.querySelectorAll('#homeScreen .home-pill, #homeScreen .home-skip').forEach(b=>b.addEventListener('click',()=>routeHome(b.dataset.act)));
-$('#homeBtn').addEventListener('click',()=>{ $('#homeScreen').hidden=false; });
+// theme: light | dark | darkest | auto (auto follows the OS light/dark setting)
+const _homeMQ=window.matchMedia?matchMedia('(prefers-color-scheme: light)'):null;
+function applyHomeTheme(){ const hs=$('#homeScreen'), pick=hGet('dwHomeTheme','darkest'), acc=hGet('dwHomeAccent','blue');
+  hs.dataset.theme = pick==='auto' ? (_homeMQ&&_homeMQ.matches?'light':'darkest') : pick;
+  hs.dataset.accent = acc;
+  document.querySelectorAll('#homeThemeSeg [role=radio]').forEach(b=>b.setAttribute('aria-checked',String(b.dataset.theme===pick)));
+  document.querySelectorAll('#homeAccent [role=radio]').forEach(b=>b.setAttribute('aria-checked',String(b.dataset.accent===acc)));
+}
+if(_homeMQ&&_homeMQ.addEventListener) _homeMQ.addEventListener('change',()=>{ if(hGet('dwHomeTheme','darkest')==='auto') applyHomeTheme(); });
+document.querySelectorAll('#homeThemeSeg [role=radio]').forEach(b=>b.addEventListener('click',()=>{ hSet('dwHomeTheme',b.dataset.theme); applyHomeTheme(); }));
+document.querySelectorAll('#homeAccent [role=radio]').forEach(b=>b.addEventListener('click',()=>{ hSet('dwHomeAccent',b.dataset.accent); applyHomeTheme(); }));
+$('#homeThemeBtn').addEventListener('click',()=>{ const p=$('#homePrefs'), open=p.hidden; p.hidden=!open; $('#homeThemeBtn').setAttribute('aria-expanded',String(open)); });
+// suggestion: samples waiting to check in beat everything; else the last action used here; else Add New SKU
+let _homeSuggest='add';
+function markSuggested(act,why){ _homeSuggest=act;
+  document.querySelectorAll('#homeScreen .home-card').forEach(c=>c.classList.toggle('suggested',c.dataset.act===act));
+  $('#homeSug').innerHTML=why?('✨ '+why):'';
+}
+async function refreshHomeSuggestion(){
+  const h=new Date().getHours();
+  $('#homeGreet').textContent=h<12?'Good morning':h<17?'Good afternoon':'Good evening';
+  $('#homeDate').textContent=new Date().toLocaleDateString(undefined,{weekday:'long',month:'short',day:'numeric'});
+  const last=hGet('dwHomeLast','');
+  const lastName={add:'Add New SKU',update:'Update SKU',lookup:'Look Up SKU',check:'Check Sample In'}[last];
+  markSuggested(lastName?last:'add', lastName?('Picking up where you left off — <b>'+lastName+'</b>'):'Start by adding a new sample');
+  const badge=$('#homeScreen .home-card[data-act=check] .badge');
+  try{
+    const ctl=new AbortController(), t=setTimeout(()=>ctl.abort(),3000);
+    const r=await(await fetch('/api/incoming-samples',{signal:ctl.signal})).json(); clearTimeout(t);
+    const n=((r&&r.samples)||[]).length;
+    badge.hidden=!n; badge.textContent=n>99?'99+':String(n);
+    if(n) markSuggested('check','<b>'+n+' sample'+(n===1?'':'s')+'</b> ordered and waiting to be checked in');
+  }catch(e){ badge.hidden=true; }   // offline / slow → keep the last-used suggestion
+  if(!$('#homeScreen').hidden && matchMedia('(hover:hover)').matches){ const c=$('#homeScreen .home-card.suggested'); if(c) c.focus({preventScroll:true}); }
+}
+function showHome(){ $('#homeScreen').hidden=false; applyHomeTheme(); refreshHomeSuggestion(); }
+document.querySelectorAll('#homeScreen .home-card, #homeScreen .home-skip').forEach(b=>b.addEventListener('click',()=>routeHome(b.dataset.act)));
+document.addEventListener('keydown',e=>{
+  if($('#homeScreen').hidden||e.metaKey||e.ctrlKey||e.altKey) return;
+  const tag=(e.target&&e.target.tagName)||''; if(/INPUT|TEXTAREA|SELECT/.test(tag)) return;
+  if(HOME_KEYS[e.key]){ e.preventDefault(); routeHome(HOME_KEYS[e.key]); }
+  else if(e.key==='Enter' && !(e.target&&e.target.closest&&e.target.closest('#homeScreen button,#homeScreen a'))){ e.preventDefault(); routeHome(_homeSuggest); }
+  else if(e.key==='Escape'){ const p=$('#homePrefs'); if(!p.hidden){ p.hidden=true; $('#homeThemeBtn').setAttribute('aria-expanded','false'); } }
+});
+$('#homeBtn').addEventListener('click',showHome);
+applyHomeTheme(); if(!$('#homeScreen').hidden) refreshHomeSuggestion();
 $('#scanvideo').addEventListener('click',resumeScan);   // tap the paused overlay to resume scanning
 $('#scanStatus').addEventListener('click',resumeScan);
 $('#scanCancel').addEventListener('click',closeLiveScan);

← 4ace5d4 Add /kid-simple/ review page: Kid-Simple mockups v1-v16 + Ph  ·  back to Dw Photo Capture  ·  Front screen: one-at-a-time swipe theme picker (Figma v1-v25 24bc0c3 →