[object Object]

← back to Wallpapercanada

wallpapercanada: also hide cards that would fall back to the generic hero

34669bb97ec303d977d4d632f9654540e8d659e0 · 2026-09-22 10:45:47 -0700 · Steve Abrams

cardImg() returns a 404 sentinel for any non-allowlisted/empty image_url so
the onerror->imgFail path hides the card. Grid, list, rail, and detail modal.
0 products affected in the current catalog (all 600 allow-listed) — defensive.

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

Files touched

Diff

commit 34669bb97ec303d977d4d632f9654540e8d659e0
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Sep 22 10:45:47 2026 -0700

    wallpapercanada: also hide cards that would fall back to the generic hero
    
    cardImg() returns a 404 sentinel for any non-allowlisted/empty image_url so
    the onerror->imgFail path hides the card. Grid, list, rail, and detail modal.
    0 products affected in the current catalog (all 600 allow-listed) — defensive.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01FZB4e67U2DRKnZu3SVFk1m
---
 public/index.html | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/public/index.html b/public/index.html
index 9b39bbf..a59e349 100644
--- a/public/index.html
+++ b/public/index.html
@@ -793,6 +793,10 @@ function safeImg(u) { u = u || ''; return (/^\/img\//.test(u) || /^https:\/\/(?:
 // Steve rule: never show a non-loading image. On <img> load failure, hide the
 // whole card/rail-tile/modal-image-wrap so a broken-image box never renders.
 function imgFail(el) { var w = el && el.closest && el.closest('.card, .rail-card, .d-img-wrap'); if (w) w.style.display = 'none'; else if (el) el.style.display = 'none'; }
+// Card image source: only a real, allow-listed image renders. Anything that
+// would otherwise fall back to the generic hero returns a 404 sentinel, so the
+// existing onerror->imgFail path hides the card (no generic-hero placeholders).
+function cardImg(u) { u = (u || '').trim(); return (/^\/img\//.test(u) || /^https:\/\/(?:cdn\.shopify\.com|designerwallcoverings\.com)\//.test(u)) ? u : '/__no-image__'; }
 // USD → CAD conversion (cosmetic; final price confirmed at checkout)
 const USD_CAD = 1.36;
 function cadPrice(usd) {
@@ -803,7 +807,7 @@ function cadPrice(usd) {
 function cardHTML(p) {
   const eager = state.page === 1;
   const cad = cadPrice(p.max_price);
-  return '<img onerror="imgFail(this)" data-f="image" loading="' + (eager ? 'eager' : 'lazy') + '"' + (eager ? ' fetchpriority="high"' : '') + ' src="' + escAttr(safeImg(p.image_url)) + '" alt="' + escAttr(p.title) + '">'
+  return '<img onerror="imgFail(this)" data-f="image" loading="' + (eager ? 'eager' : 'lazy') + '"' + (eager ? ' fetchpriority="high"' : '') + ' src="' + escAttr(cardImg(p.image_url)) + '" alt="' + escAttr(p.title) + '">'
     + '<div class="overlay">'
     + '<div class="pat" data-f="pattern">' + escAttr(p.pattern_name || p.title) + '</div>'
     + '<div class="ven" data-f="vendor">Designer Wallcoverings' + (cad ? ' · <span class="cad-price">from ' + cad + ' CAD</span>' : '') + '</div>'
@@ -818,7 +822,7 @@ function rowHTML(p) {
   const mp = Number(p.max_price);
   const price = (p.max_price != null && !isNaN(mp) && mp > 0) ? '$' + mp.toFixed(2) : '—';
   const aes = LABELS[p.aesthetic] || p.aesthetic || '—';
-  return '<img onerror="imgFail(this)" data-f="image" loading="lazy" src="' + escAttr(safeImg(p.image_url)) + '" alt="' + escAttr(p.title) + '">'
+  return '<img onerror="imgFail(this)" data-f="image" loading="lazy" src="' + escAttr(cardImg(p.image_url)) + '" alt="' + escAttr(p.title) + '">'
     + '<div class="r-pat" data-f="pattern">' + escAttr(p.pattern_name || p.title) + '</div>'
     + '<div class="r-col r-sku" data-f="vendor">' + escAttr(cleanSku(p.sku || p.handle_display || p.handle) || '—') + '</div>'
     + '<div class="r-col r-aes" data-f="color">' + (p.aesthetic ? sfDrill(p.aesthetic, String(aes).replace(/-/g, ' '), 'span') : escAttr(String(aes).replace(/-/g, ' '))) + '</div>'
@@ -845,7 +849,7 @@ function openDetails(p) {
   const dImg = m.querySelector('[data-d-img]');
   const dWrap = dImg.closest('.d-img-wrap'); if (dWrap) dWrap.style.display = ''; // reset (element is reused across opens)
   dImg.onerror = function () { imgFail(this); };
-  dImg.src = safeP.image_url;
+  dImg.src = cardImg(safeP.image_url);
   dImg.alt = safeP.title;
   m.querySelector('[data-d-pat]').textContent = safeP.pattern;
   m.querySelector('[data-d-brand]').textContent = 'Designer Wallcoverings' + (cleanSku(safeP.sku) ? ' · ' + cleanSku(safeP.sku) : '');
@@ -1522,7 +1526,7 @@ document.addEventListener('DOMContentLoaded', function(){ try { renderRailSectio
       const url = (p.image_url || '').replace(/(_\d+x\d*)(\.(jpg|jpeg|png|webp|gif))(\?|$)/i, '$2$4');
       const cad = (typeof cadPrice === 'function') ? cadPrice(p.max_price) : '';
       return '<a class="rail-card" href="/sample/' + encodeURIComponent(p.handle_display || p.handle || p.sku) + '">' +
-        '<img onerror="imgFail(this)" src="' + url + '" alt="' + (p.title || '').replace(/"/g, '&quot;') + '" loading="lazy">' +
+        '<img onerror="imgFail(this)" src="' + cardImg(url) + '" alt="' + (p.title || '').replace(/"/g, '&quot;') + '" loading="lazy">' +
         '<div class="rc-title">' + ((p.title || '').split('|')[0] || '').trim() + '</div>' +
         '<div class="rc-meta">Designer Wallcoverings' + (cad ? ' · ' + cad + ' CAD' : '') + '</div>' +
       '</a>';

← a5322e0 wallpapercanada: never show non-loading images (hide card/ra  ·  back to Wallpapercanada  ·  front-page-steward: ux-primitives bundle (silent) TK-12031 847b96c →