[object Object]

← back to Wallco Ai

YOLO R3 — add diff-bar chip strip to /age-themes bands. Auto-detects which props changed between current↔best (font/body/h/accent/etc.) and renders them as 'key from → to' chips above the compare grid. Fixes architect punch-list #11 (Adult band's subtle body 13→15 delta now reads explicitly instead of looking 'identical').

444eefcb532922325ab1bb863970db31241afa32 · 2026-05-12 00:39:58 -0700 · Steve Abrams

Files touched

Diff

commit 444eefcb532922325ab1bb863970db31241afa32
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue May 12 00:39:58 2026 -0700

    YOLO R3 — add diff-bar chip strip to /age-themes bands. Auto-detects which props changed between current↔best (font/body/h/accent/etc.) and renders them as 'key from → to' chips above the compare grid. Fixes architect punch-list #11 (Adult band's subtle body 13→15 delta now reads explicitly instead of looking 'identical').
---
 YOLO_BACKLOG.md |  5 +++--
 server.js       | 23 +++++++++++++++++++++++
 2 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/YOLO_BACKLOG.md b/YOLO_BACKLOG.md
index c5740bd..27ed574 100644
--- a/YOLO_BACKLOG.md
+++ b/YOLO_BACKLOG.md
@@ -28,7 +28,7 @@ User ask verbatim: "show final patterns and 1 room setting on https://wallco.ai/
 
 - [x] **R1 · Screenshot the isolate modal** — 2026-05-12 00:14 · 6 screenshots saved to tmp/isolate-qa/ · works: palette+SW labels render correctly · issues found: modal bottom cut at 900vh, review-card image-wrap aspect ratio off · *no commit (screenshots only)*
 - [ ] **R2 · ΔE closeness bands** — `/age-themes` and `/api/isolate` response both expose raw ΔE numbers. Add a human-readable closeness band per swatch: ΔE 0-5 "exact", 5-15 "close", 15-30 "approximate", 30+ "not in SW catalog (paint physics ceiling)". Render in both `/age-themes` and the isolate modal.
-- [ ] **R3 · Adult band single-swatch fix** — `/age-themes` adult band has identical current+best fonts (-apple-system kept). Currently renders two identical swatches. Per architect punch-list item #11: render as a single swatch with "no change recommended" label.
+- [x] **R3 · Adult band diff-chip clarity** — 2026-05-12 00:48 · resolved differently than architect spec'd. Adult band IS visually subtle (only body 13→15 changes) but renders TWO swatches because the body delta is real. Added explicit `diff-bar` chip strip above every band's compare grid showing each changed prop in `key from → to` format (e.g. Adult: "1 change: body 13 → 15"; Teen: "3 changes: font/body/h"). Diff detection auto-derived from `Object.keys(b.best).filter(k => b.current[k] !== b.best[k])` — single source of truth, no hardcoded annotations. Bands with zero diffs (none today) would render "no change recommended" placeholder. Verified live for all 8 bands.
 - [ ] **R4 · Isolate keyboard shortcuts** — add `W` / `P` to flip wallpaper/paint mode inside the open isolate modal. Add `C` to copy primary hex to clipboard. Document in the ? toast.
 - [ ] **R5 · Isolate modal: source-design SW label** — currently the palette[0] hex shows its SW match, but the design's stored `dominant_hex` (which may differ from extraction) isn't labeled. Show both: "Design's `dominant_hex` = X (SW Y)" + "Extracted palette[0] = Z (SW W)".
 
@@ -68,4 +68,5 @@ TICK · TIMESTAMP · ITEM · STATUS · COMMIT
 - T6 · 2026-05-12 00:27 · R12 visual-regression baselines · done · tests/integration/isolate-visual.spec.js (3 PNGs, 12.7s suite)
 - T7 · 2026-05-12 00:28 · R10 tsd mobile QA · done · no fix needed (verified at 390×844)
 - T8 · 2026-05-12 00:35 · R11 dominant_sw · done · /api/isolate response now annotates dominant_hex with SW match
-- T9 · 2026-05-12 00:42 · R9 AI-language scrub · done · 9 public surfaces · admin banner kept per validation-loop
+- T9 · 2026-05-12 00:42 · R9 AI-language scrub · done · 9 public surfaces · admin banner kept per validation-loop · 9faa35b
+- T10 · 2026-05-12 00:48 · R3 Adult diff-chip · done · auto-detect changed props per band, render diff-bar above compare grid
diff --git a/server.js b/server.js
index 6452169..9aa3e42 100644
--- a/server.js
+++ b/server.js
@@ -3743,6 +3743,21 @@ app.get('/age-themes', (req, res) => {
   const bands = AGE_THEME_DATA.bands;
 
   const renderBand = (b) => {
+    // Diff chips — surface every prop that changed between current ↔ best so
+    // even subtle deltas (e.g. body +2px with font/colors unchanged) read at a
+    // glance. Fixes architect punch-list #11 (adult band "looked identical").
+    const diffProps = Object.keys(b.best).filter(k => b.current[k] !== b.best[k]);
+    const diffChips = diffProps.map(k => {
+      const cur = b.current[k], next = b.best[k];
+      const isFontFamily = k === 'font';
+      const lhs = isFontFamily ? String(cur).split(',')[0] : cur;
+      const rhs = isFontFamily ? String(next).split(',')[0] : next;
+      return `<span class="diff-chip"><span class="diff-key">${k}</span> <span class="diff-from">${lhs}</span> → <span class="diff-to">${rhs}</span></span>`;
+    }).join('');
+    const diffBar = diffProps.length === 0
+      ? `<div class="diff-bar diff-bar-noop">no change recommended</div>`
+      : `<div class="diff-bar"><span class="diff-count">${diffProps.length} change${diffProps.length===1?'':'s'}:</span>${diffChips}</div>`;
+
     const swatch = (theme) => `
       <div class="theme-card" style="background:${theme.bg}; color:${theme.fg}; border:1px solid ${theme.border}; font-family:${theme.font};">
         <div class="theme-panel" style="background:${theme.panel}; border:1px solid ${theme.border};">
@@ -3778,6 +3793,7 @@ app.get('/age-themes', (req, res) => {
           <h2><span class="age-pill">${b.age}</span> ${b.label}</h2>
           <p class="band-mood">${b.mood}</p>
           <p class="band-anchor">anchor → ${b.anchor}</p>
+          ${diffBar}
         </header>
         <div class="band-compare">
           <div class="theme-col">
@@ -3821,6 +3837,13 @@ app.get('/age-themes', (req, res) => {
   .age-pill { display:inline-block; padding:3px 10px; background:#d2b15c; color:#0f0e0c; border-radius:14px; font-size:11px; font-weight:600; letter-spacing:.04em; }
   .band-mood { margin:0 0 4px; color:#bba; font-size:12px; font-style:italic; }
   .band-anchor { margin:0; color:#888; font-size:11px; }
+  .diff-bar { margin-top:8px; padding:8px 12px; background:rgba(210,177,92,.06); border-left:3px solid #d2b15c; border-radius:2px; display:flex; gap:6px; flex-wrap:wrap; align-items:center; }
+  .diff-bar-noop { background:rgba(255,255,255,.03); border-left-color:#3a3631; color:#666; font-size:11px; font-style:italic; }
+  .diff-count { font-size:10px; color:#d2b15c; letter-spacing:.06em; text-transform:uppercase; margin-right:4px; }
+  .diff-chip { font-size:11px; padding:2px 8px; background:#1a1816; border:1px solid #2a2825; border-radius:11px; color:#bba; font-family:ui-monospace, Menlo, monospace; }
+  .diff-chip .diff-key { color:#888; }
+  .diff-chip .diff-from { color:#d2554a; }
+  .diff-chip .diff-to { color:#5fbf6e; font-weight:600; }
   .band-compare { display:grid; grid-template-columns: 1fr 1fr 1.1fr; gap:14px; align-items:start; }
   .theme-col { display:flex; flex-direction:column; gap:6px; }
   .theme-label { font-size:10px; letter-spacing:.1em; color:#888; text-transform:uppercase; padding-left:4px; }

← 8ad112a YOLO R9 — scrub AI-generated language from 9 public surfaces  ·  back to Wallco Ai  ·  YOLO R4 — keyboard shortcuts on isolate modal (W=wallpaper, 55f8ac2 →