[object Object]

← back to Designer Wallcoverings

TK-11240: repoint Other Colorways module off title-match onto custom.pattern_name

2e8e01e530ec68c882426fb83c90ba17dcdf1290 · 2026-09-11 08:35:52 -0700 · Steve

Live theme 145556635699 (main). Adds templates/search.colorways.liquid (JSON view
emitting each sibling custom.pattern_name+custom.color, paginated) and rewrites
snippets/colorway-siblings.liquid to group by exact pattern_name (title fallback),
drop the 10/24 truncation (verified 60 vs 10 siblings live on Faux Leaf Squares),
label from custom.color w/ SKU fallback (never 'View'). Part B (versa_catalog
backfill) DROPPED by Steve at CLARIFY gate: source proven ~89% wrong vs live.
Undo: restore snippet from theme-backups + delete the search.colorways template.

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

Files touched

Diff

commit 2e8e01e530ec68c882426fb83c90ba17dcdf1290
Author: Steve <steve@designerwallcoverings.com>
Date:   Fri Sep 11 08:35:52 2026 -0700

    TK-11240: repoint Other Colorways module off title-match onto custom.pattern_name
    
    Live theme 145556635699 (main). Adds templates/search.colorways.liquid (JSON view
    emitting each sibling custom.pattern_name+custom.color, paginated) and rewrites
    snippets/colorway-siblings.liquid to group by exact pattern_name (title fallback),
    drop the 10/24 truncation (verified 60 vs 10 siblings live on Faux Leaf Squares),
    label from custom.color w/ SKU fallback (never 'View'). Part B (versa_catalog
    backfill) DROPPED by Steve at CLARIFY gate: source proven ~89% wrong vs live.
    Undo: restore snippet from theme-backups + delete the search.colorways template.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01Y6Dz4NMwA1ekVm591JgfPT
---
 .../theme-files/snippets/colorway-siblings.liquid  | 189 ++++++++++++---------
 .../theme-files/templates/search.colorways.liquid  |  23 +++
 2 files changed, 130 insertions(+), 82 deletions(-)

diff --git a/shopify/theme-files/snippets/colorway-siblings.liquid b/shopify/theme-files/snippets/colorway-siblings.liquid
index 40613792..fc681da6 100644
--- a/shopify/theme-files/snippets/colorway-siblings.liquid
+++ b/shopify/theme-files/snippets/colorway-siblings.liquid
@@ -1,9 +1,12 @@
 {% comment %}
-  Colorway Siblings — shows other colors of the same pattern as clickable thumbnails
-  Uses dwc.pattern_name metafield or parses from title
+  Colorway Siblings — shows other colors of the same pattern as clickable thumbnails.
+  TK-11240 rewrite: groups by the custom.pattern_name metafield (title-prefix fallback),
+  reads the full sibling set via the `colorways` search JSON view (no 10/24 truncation),
+  and labels each swatch from custom.color / dwc.color (title- or SKU-derived fallback),
+  never the literal string "View". Fails safe: any error just hides the section.
 {% endcomment %}
 
-{%- assign pattern_name = product.metafields.custom.pattern_name.value | default: product.metafields.dwc.pattern_name.value | default: product.metafields.custom.design_name.value -%}
+{%- assign pattern_name = product.metafields.custom.pattern_name.value | default: product.metafields.dwc.pattern_name.value | default: product.metafields.global.pattern_name.value | default: product.metafields.custom.design_name.value -%}
 
 {%- unless pattern_name != blank -%}
   {%- assign pattern_parts = product.title | split: " - " -%}
@@ -34,84 +37,106 @@
   (function() {
     var currentHandle = {{ product.handle | json }};
     var currentVendor = {{ product.vendor | json }};
-    var searchTerm = {{ pattern_name | json }};
-    if (!searchTerm || searchTerm.length < 3) return;
-
-    fetch('/search/suggest.json?q=' + encodeURIComponent(searchTerm) + '&resources[type]=product&resources[limit]=24', { credentials: 'same-origin' })
-      .then(function(r) { return r.ok ? r.json() : null; })
-      .then(function(data) {
-        if (!data || !data.resources || !data.resources.results) return;
-        var products = data.resources.results.products || [];
-        var matchLower = searchTerm.toLowerCase();
-        var container = document.getElementById('dw-colorway-thumbs');
-        var section = document.getElementById('dw-colorway-siblings');
-        if (!container || !section) return;
-
-        var siblings = [];
-        for (var i = 0; i < products.length; i++) {
-          var p = products[i];
-          /* Skip current product */
-          if (p.handle === currentHandle) continue;
-          /* Must match pattern name in title */
-          if ((p.title || '').toLowerCase().indexOf(matchLower) < 0) continue;
-          /* Must match vendor to avoid cross-vendor contamination */
-          if (currentVendor && (p.vendor || '').toLowerCase() !== currentVendor.toLowerCase()) continue;
-          /* Must have an image */
-          if (!p.image && !(p.featured_image && p.featured_image.url)) continue;
-
-          var imgUrl = (p.featured_image && p.featured_image.url) ? p.featured_image.url : p.image;
-
-          /* Extract color: remove pattern name and vendor */
-          var label = (p.title || '');
-          var pipeIdx = label.indexOf(' | ');
-          if (pipeIdx > 0) label = label.substring(0, pipeIdx);
-          var dashIdx = label.indexOf(' - ');
-          if (dashIdx > 0) {
-            label = label.substring(dashIdx + 3).trim();
-          } else {
-            var pLower = label.toLowerCase().indexOf(matchLower);
-            if (pLower === 0) label = label.substring(searchTerm.length).trim();
-          }
-          label = label.replace(/\s*(Wallcoverings?|Fabrics?|Murals?|Commercial)\s*/gi, '').trim();
-          if (!label) label = 'View';
-
-          siblings.push({ color: label, image: imgUrl, url: p.url || ('/products/' + p.handle) });
-        }
-
-        if (siblings.length === 0) return;
-        siblings.sort(function(a, b) { return a.color.localeCompare(b.color); });
-
-        for (var j = 0; j < siblings.length; j++) {
-          var s = siblings[j];
-          var a = document.createElement('a');
-          a.href = s.url;
-          a.title = s.color;
-          a.style.cssText = 'display:flex;flex-direction:column;align-items:center;gap:4px;text-decoration:none;width:72px;';
-
-          var wrap = document.createElement('div');
-          wrap.style.cssText = 'width:64px;height:64px;border-radius:8px;overflow:hidden;border:2px solid #e5e2dc;transition:border-color 0.15s,box-shadow 0.15s;';
-          a.onmouseover = function() { this.firstChild.style.borderColor = '#3D4246'; this.firstChild.style.boxShadow = '0 2px 8px rgba(0,0,0,0.1)'; };
-          a.onmouseout = function() { this.firstChild.style.borderColor = '#e5e2dc'; this.firstChild.style.boxShadow = 'none'; };
-
-          var img = document.createElement('img');
-          img.src = s.image;
-          img.alt = s.color;
-          img.loading = 'lazy';
-          img.style.cssText = 'width:100%;height:100%;object-fit:cover;';
-          wrap.appendChild(img);
-          a.appendChild(wrap);
-
-          var nameSpan = document.createElement('span');
-          nameSpan.textContent = s.color;
-          nameSpan.style.cssText = 'font-size:10px;color:#666;line-height:1.2;max-width:72px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;text-align:center;';
-          a.appendChild(nameSpan);
-
-          container.appendChild(a);
-        }
-
-        section.style.display = '';
-      })
-      .catch(function() {});
+    var patternName   = {{ pattern_name | json }};
+    if (!patternName || patternName.length < 3) return;
+
+    function norm(s){ return (s||'').toString().replace(/\s+/g,' ').trim().toLowerCase(); }
+
+    /* Pattern name from a sibling's title when it has no metafield (the ~234 legacy set). */
+    function titlePattern(t){
+      t = t || '';
+      var seps = [' - ', ' – ', ' | '];
+      for (var i=0;i<seps.length;i++){ var ix=t.indexOf(seps[i]); if (ix>0) return t.substring(0,ix).trim(); }
+      return t.trim();
+    }
+
+    /* Human colour label — never the literal "View". */
+    function colorLabel(p){
+      if (p.color && p.color.trim()) return p.color.trim();
+      var t = p.title || '';
+      var pipe = t.indexOf(' | '); if (pipe>0) t = t.substring(0,pipe);
+      var lbl = '';
+      var dash = t.indexOf(' - ');
+      if (dash>0) { lbl = t.substring(dash+3).trim(); }
+      else {
+        var pi = norm(t).indexOf(norm(patternName));
+        if (pi===0) lbl = t.substring(patternName.length).trim();
+      }
+      lbl = lbl.replace(/\s*(Wallcoverings?|Fabrics?|Murals?|Commercial)\s*/gi,'').trim();
+      if (lbl) return lbl;
+      /* SKU colour-code fallback (base SKU, strip -yard/-sample) */
+      if (p.sku){ var base = p.sku.replace(/-(yard|sample)$/i,''); if (base) return base; }
+      return (p.title || '').trim() || patternName;
+    }
+
+    var wantPattern = norm(patternName);
+    var wantVendor  = norm(currentVendor);
+    var MAX_PAGES = 8; /* 8*50 = 400 siblings ceiling; no pattern is that large */
+
+    function fetchPage(q, n){
+      var url = '/search?type=product&view=colorways&page=' + n + '&q=' + encodeURIComponent(q);
+      return fetch(url, { credentials:'same-origin' })
+        .then(function(r){ return r.ok ? r.text() : null; })
+        .then(function(txt){ if(!txt) return null; try { return JSON.parse(txt); } catch(e){ return null; } });
+    }
+
+    /* Quoted phrase keeps the exact-pattern siblings clustered in the top pages
+       (unquoted spreads them across ~20 relevance-ranked pages). Fall back to
+       unquoted only if the quoted phrase returns nothing. */
+    fetchPage('"' + patternName + '"', 1).then(function(first){
+      if (first && first.products && first.products.length) return { q:'"'+patternName+'"', first:first };
+      return fetchPage(patternName, 1).then(function(f2){ return { q:patternName, first:f2 }; });
+    }).then(function(ctx){
+      var first = ctx.first;
+      if (!first || !first.products) return null;
+      var pages = Math.min(first.pages || 1, MAX_PAGES);
+      if (pages <= 1) return [first];
+      var rest = [];
+      for (var n=2; n<=pages; n++) rest.push(fetchPage(ctx.q, n));
+      return Promise.all(rest).then(function(more){ return [first].concat(more.filter(Boolean)); });
+    }).then(function(pageObjs){
+      if (!pageObjs) return;
+      var seen = {}, siblings = [];
+      pageObjs.forEach(function(pg){
+        (pg.products || []).forEach(function(p){
+          if (!p || !p.handle || seen[p.handle]) return;
+          if (p.handle === currentHandle) return;
+          if (!p.image) return;
+          if (wantVendor && norm(p.vendor) !== wantVendor) return;
+          var sibPattern = norm(p.pattern_name) || norm(titlePattern(p.title));
+          if (sibPattern !== wantPattern) return;
+          seen[p.handle] = true;
+          siblings.push({ color: colorLabel(p), image: p.image, url: p.url || ('/products/' + p.handle) });
+        });
+      });
+      if (!siblings.length) return;
+      siblings.sort(function(a,b){ return a.color.localeCompare(b.color); });
+
+      var container = document.getElementById('dw-colorway-thumbs');
+      var section   = document.getElementById('dw-colorway-siblings');
+      if (!container || !section) return;
+
+      for (var j=0; j<siblings.length; j++){
+        var s = siblings[j];
+        var a = document.createElement('a');
+        a.href = s.url; a.title = s.color;
+        a.style.cssText = 'display:flex;flex-direction:column;align-items:center;gap:4px;text-decoration:none;width:72px;';
+        var wrap = document.createElement('div');
+        wrap.style.cssText = 'width:64px;height:64px;border-radius:8px;overflow:hidden;border:2px solid #e5e2dc;transition:border-color 0.15s,box-shadow 0.15s;';
+        a.onmouseover = function(){ this.firstChild.style.borderColor='#3D4246'; this.firstChild.style.boxShadow='0 2px 8px rgba(0,0,0,0.1)'; };
+        a.onmouseout  = function(){ this.firstChild.style.borderColor='#e5e2dc'; this.firstChild.style.boxShadow='none'; };
+        var img = document.createElement('img');
+        img.src = s.image; img.alt = s.color; img.loading='lazy';
+        img.style.cssText = 'width:100%;height:100%;object-fit:cover;';
+        wrap.appendChild(img); a.appendChild(wrap);
+        var nameSpan = document.createElement('span');
+        nameSpan.textContent = s.color;
+        nameSpan.style.cssText = 'font-size:10px;color:#666;line-height:1.2;max-width:72px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;text-align:center;';
+        a.appendChild(nameSpan);
+        container.appendChild(a);
+      }
+      section.style.display = '';
+    }).catch(function(){});
   })();
   </script>
-{%- endif -%}
\ No newline at end of file
+{%- endif -%}
diff --git a/shopify/theme-files/templates/search.colorways.liquid b/shopify/theme-files/templates/search.colorways.liquid
new file mode 100644
index 00000000..d96eb36b
--- /dev/null
+++ b/shopify/theme-files/templates/search.colorways.liquid
@@ -0,0 +1,23 @@
+{% comment %}
+  TK-11240 — Colorways search JSON view.
+  Emits sibling products for the "Other Colorways" module WITH their real metafields
+  (custom.pattern_name / custom.color) so the module can group by pattern_name exactly
+  and label swatches by color — neither of which /search/suggest.json exposes.
+  Fetched by snippets/colorway-siblings.liquid as:
+    /search?q=<pattern>&type=product&view=colorways&page=N
+  Paginated (50/page) so the full sibling set is reachable (no 10/24 truncation).
+{% endcomment %}
+{%- layout none -%}
+{%- paginate search.results by 50 -%}
+{"page":{{ paginate.current_page }},"pages":{{ paginate.pages }},"total":{{ search.results_count | default: 0 }},"products":[
+{%- assign first = true -%}
+{%- for item in search.results -%}
+  {%- if item.object_type == 'product' -%}
+    {%- unless first %},{% endunless -%}{%- assign first = false -%}
+    {%- assign pn = item.metafields.custom.pattern_name.value | default: item.metafields.dwc.pattern_name.value | default: item.metafields.global.pattern_name.value -%}
+    {%- assign cl = item.metafields.custom.color.value | default: item.metafields.dwc.color.value -%}
+    {"handle":{{ item.handle | json }},"url":{{ item.url | json }},"title":{{ item.title | json }},"vendor":{{ item.vendor | json }},"image":{%- if item.featured_image -%}{{ item.featured_image | image_url: width: 200 | json }}{%- else -%}null{%- endif -%},"pattern_name":{{ pn | json }},"color":{{ cl | json }},"sku":{{ item.variants.first.sku | json }}}
+  {%- endif -%}
+{%- endfor -%}
+]}
+{%- endpaginate -%}

← 480c254a Stock new cadence drafts with verified inventory and scoped  ·  back to Designer Wallcoverings  ·  auto-data-snapshot: 2026-09-11T08:57:34 (21 data files) — DW eca00056 →