[object Object]

← back to Wallco Ai

ui: surface ALL styles + populated color dots on /designs grid — drop motif slice(0,12), expand hue palette from 10 to 32 named buckets with finer hue ranges + lightness subtypes (blush/coral/butter/lime/sky/lavender/burgundy/terracotta/chocolate/cream/linen/ivory/stone/charcoal/ink/black/white)

9cf8d04a78eeaf2716c61c816a6c31596d58b87d · 2026-05-20 10:25:21 -0700 · Steve Abrams

Files touched

Diff

commit 9cf8d04a78eeaf2716c61c816a6c31596d58b87d
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed May 20 10:25:21 2026 -0700

    ui: surface ALL styles + populated color dots on /designs grid — drop motif slice(0,12), expand hue palette from 10 to 32 named buckets with finer hue ranges + lightness subtypes (blush/coral/butter/lime/sky/lavender/burgundy/terracotta/chocolate/cream/linen/ivory/stone/charcoal/ink/black/white)
---
 server.js | 62 +++++++++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 43 insertions(+), 19 deletions(-)

diff --git a/server.js b/server.js
index a54db42..f39ba5a 100644
--- a/server.js
+++ b/server.js
@@ -3512,24 +3512,40 @@ app.get('/designs', (req, res) => {
     if (!hex || !/^#[0-9a-fA-F]{6}$/.test(hex)) return null;
     const r = parseInt(hex.slice(1,3),16)/255, g = parseInt(hex.slice(3,5),16)/255, b = parseInt(hex.slice(5,7),16)/255;
     const max = Math.max(r,g,b), min = Math.min(r,g,b);
-    if (max === min) return 'neutral';
+    const l = (max + min) / 2;
+    const s = max === min ? 0 : (max - min) / (l > 0.5 ? 2 - max - min : max + min);
+    // ── achromatic / near-neutral first, by lightness ────────────────────
+    if (s < 0.12) {
+      if (l < 0.08) return 'black';
+      if (l < 0.18) return 'ink';
+      if (l < 0.32) return 'charcoal';
+      if (l < 0.50) return 'stone';
+      if (l < 0.72) return 'neutral';
+      if (l < 0.86) return 'linen';
+      if (l < 0.94) return 'cream';
+      return 'white';
+    }
+    // ── chromatic — finer hue ranges + lightness-based subtypes ──────────
     let h = 0; const d = max - min;
     if (max === r) h = (g-b)/d + (g<b?6:0);
     else if (max === g) h = (b-r)/d + 2;
     else h = (r-g)/d + 4;
     h = (h * 60 + 360) % 360;
-    const l = (max + min) / 2;
-    const s = (max - min) / (l > 0.5 ? 2 - max - min : max + min);
-    if (s < 0.1) return 'neutral';
-    if (h < 15 || h >= 345) return 'rose';
-    if (h < 40)  return 'amber';
-    if (h < 60)  return 'honey';
-    if (h < 90)  return 'olive';
-    if (h < 160) return 'sage';
-    if (h < 200) return 'marine';
-    if (h < 250) return 'sapphire';
-    if (h < 290) return 'mauve';
-    return 'plum';
+    if (h < 12 || h >= 350) return l > 0.65 ? 'blush' : (l < 0.30 ? 'burgundy' : 'rose');
+    if (h < 22)  return l < 0.30 ? 'chocolate' : (l > 0.65 ? 'coral' : 'terracotta');
+    if (h < 38)  return l > 0.70 ? 'butter' : 'amber';
+    if (h < 50)  return l > 0.70 ? 'butter' : (l < 0.40 ? 'chocolate' : 'honey');
+    if (h < 62)  return l > 0.70 ? 'butter' : (l < 0.40 ? 'olive' : 'gold');
+    if (h < 80)  return l > 0.70 ? 'lime' : 'olive';
+    if (h < 130) return l > 0.70 ? 'lime' : (l < 0.35 ? 'olive' : 'sage');
+    if (h < 160) return l < 0.35 ? 'emerald' : 'sage';
+    if (h < 180) return 'teal';
+    if (h < 205) return l > 0.65 ? 'sky' : 'marine';
+    if (h < 230) return l > 0.65 ? 'sky' : 'sapphire';
+    if (h < 260) return l < 0.35 ? 'indigo' : 'sapphire';
+    if (h < 285) return l > 0.70 ? 'lavender' : 'mauve';
+    if (h < 320) return l > 0.65 ? 'lavender' : (l < 0.35 ? 'plum' : 'plum');
+    return l > 0.55 ? 'magenta' : 'burgundy';
   }
   // Hard filter — never render a card whose image file isn't on disk OR whose
   // id is in the blank-guard. /api/designs already does this; mirror it on the
@@ -3586,16 +3602,24 @@ app.get('/designs', (req, res) => {
     .filter(c => c && !VENDOR_DENYLIST.test(c))
     .sort();
 
-  // Top motifs across full catalog (not filtered)
+  // ALL motifs/styles across full catalog (was limited to 12 — show every style with ≥1 design)
   const motifCounts = {};
   DESIGNS.forEach(d => (d.motifs || []).forEach(m => { motifCounts[m] = (motifCounts[m]||0) + 1; }));
-  const topMotifs = Object.entries(motifCounts).sort((a,b)=>b[1]-a[1]).slice(0,12);
+  const topMotifs = Object.entries(motifCounts).sort((a,b)=>b[1]-a[1]);  // no slice — show all
 
+  // Populated hue palette — every named hue bucket gets a dot (was 10, now full spectrum)
   const HUE_DEFS = [
-    ['rose',     '#e08291'], ['amber',   '#c97b3a'], ['honey',   '#d2a44a'],
-    ['olive',    '#8a8c4a'], ['sage',    '#8aa17a'], ['marine',  '#6aa6b6'],
-    ['sapphire', '#5a82a8'], ['mauve',   '#a08bbf'], ['plum',    '#8e5887'],
-    ['neutral',  '#b8b3a8']
+    ['rose',      '#e08291'], ['blush',     '#e8b5b8'], ['coral',     '#e6856b'],
+    ['amber',     '#c97b3a'], ['honey',     '#d2a44a'], ['gold',      '#caa75a'],
+    ['butter',    '#e8d585'], ['olive',     '#8a8c4a'], ['lime',      '#a8c45a'],
+    ['sage',      '#8aa17a'], ['emerald',   '#3a8a5a'], ['teal',      '#4a8a8a'],
+    ['marine',    '#6aa6b6'], ['sky',       '#7ab8d8'], ['sapphire',  '#5a82a8'],
+    ['indigo',    '#4a4a8a'], ['mauve',     '#a08bbf'], ['lavender',  '#c8bce0'],
+    ['plum',      '#8e5887'], ['magenta',   '#b04a7a'], ['burgundy',  '#7a2838'],
+    ['terracotta','#a85a3a'], ['chocolate', '#5a3826'], ['cream',     '#ede6d0'],
+    ['linen',     '#dccfb6'], ['ivory',     '#f0e8d8'], ['neutral',   '#b8b3a8'],
+    ['stone',     '#8a847a'], ['charcoal',  '#3a3a3a'], ['ink',       '#1a1816'],
+    ['white',     '#f8f6f0'], ['black',     '#0a0808']
   ];
   const hueCounts = {};
   DESIGNS.forEach(d => { const b = hueBucketOf(d.dominant_hex); if (b) hueCounts[b] = (hueCounts[b]||0)+1; });

← b4ba6e1 rename: Magic → Curated across all customer-facing copy on w  ·  back to Wallco Ai  ·  wallco mural: viewport 20:11 + cover-fit init at zoom 1 615bf37 →