← back to Designer Wallcoverings
Collection hero fix: harden image/no-image switch via body.dw-collection-has-hero; F3 mobile gap verified (branding 118->74px); confirm JS errors owned by companion grid.js memo
fff83e00444faa6041a3fce36cd17ac7939d810e · 2026-06-23 15:16:53 -0700 · Steve Studio
Files touched
A shopify/collection-hero-fix/console-probe.jsM shopify/collection-hero-fix/dw-collection-hero.cssA shopify/collection-hero-fix/probe-collection-defects.jsA shopify/collection-hero-fix/verify-collection-hero-fix.js
Diff
commit fff83e00444faa6041a3fce36cd17ac7939d810e
Author: Steve Studio <steve@designerwallcoverings.com>
Date: Tue Jun 23 15:16:53 2026 -0700
Collection hero fix: harden image/no-image switch via body.dw-collection-has-hero; F3 mobile gap verified (branding 118->74px); confirm JS errors owned by companion grid.js memo
---
shopify/collection-hero-fix/console-probe.js | 13 ++++
shopify/collection-hero-fix/dw-collection-hero.css | 4 +-
.../probe-collection-defects.js | 74 ++++++++++++++++++
.../verify-collection-hero-fix.js | 91 ++++++++++++++++++++++
4 files changed, 180 insertions(+), 2 deletions(-)
diff --git a/shopify/collection-hero-fix/console-probe.js b/shopify/collection-hero-fix/console-probe.js
new file mode 100644
index 00000000..33a6ab93
--- /dev/null
+++ b/shopify/collection-hero-fix/console-probe.js
@@ -0,0 +1,13 @@
+const { chromium } = require('playwright');
+(async () => {
+ const b = await chromium.launch();
+ const p = await (await b.newContext({viewport:{width:1440,height:900}})).newPage();
+ const errs = [];
+ p.on('console', m => { if (m.type()==='error') errs.push(m.text()); });
+ p.on('pageerror', e => errs.push('PAGEERROR: '+e.message));
+ await p.goto('https://www.designerwallcoverings.com/collections/all',{waitUntil:'domcontentloaded',timeout:60000});
+ await p.waitForTimeout(4000);
+ console.log('CONSOLE ERRORS ('+errs.length+'):');
+ [...new Set(errs)].slice(0,15).forEach(e=>console.log(' -', e.slice(0,170)));
+ await b.close();
+})();
diff --git a/shopify/collection-hero-fix/dw-collection-hero.css b/shopify/collection-hero-fix/dw-collection-hero.css
index 5cd61ab6..006e73cd 100644
--- a/shopify/collection-hero-fix/dw-collection-hero.css
+++ b/shopify/collection-hero-fix/dw-collection-hero.css
@@ -83,7 +83,7 @@ body.template-collection .boost-sd__product-filter-fallback .page-title {
(set only when collection.image OR settings.collection_default_hero exists)
AND Boost rendered no native image-inner. Excludes .dw-has-hero so the
image case (painted by the snippet's inline <style>) is never stripped. */
-.boost-sd__collection-header:not(.dw-has-hero):not(:has(.boost-sd__header-image-inner)) .boost-sd__header-title {
+body:not(.dw-collection-has-hero) .boost-sd__collection-header:not(:has(.boost-sd__header-image-inner)) .boost-sd__header-title {
background: transparent !important;
color: #1a1a1a !important;
border-radius: 0 !important;
@@ -93,7 +93,7 @@ body.template-collection .boost-sd__product-filter-fallback .page-title {
text-transform: uppercase;
font-weight: 300;
}
-.boost-sd__collection-header:not(.dw-has-hero):not(:has(.boost-sd__header-image-inner)) {
+body:not(.dw-collection-has-hero) .boost-sd__collection-header:not(:has(.boost-sd__header-image-inner)) {
background: #f4f2ee;
min-height: 200px;
display: flex;
diff --git a/shopify/collection-hero-fix/probe-collection-defects.js b/shopify/collection-hero-fix/probe-collection-defects.js
new file mode 100644
index 00000000..083c1c5e
--- /dev/null
+++ b/shopify/collection-hero-fix/probe-collection-defects.js
@@ -0,0 +1,74 @@
+// Probe live /collections/all to identify the grey-pill hero element + mobile header gap.
+// Read-only: no clicks, no writes. $0 (local).
+const { chromium } = require('playwright');
+
+(async () => {
+ const url = 'https://www.designerwallcoverings.com/collections/all';
+ const browser = await chromium.launch();
+
+ // ---- DESKTOP: find the grey pill behind "Products" ----
+ const ctxD = await browser.newContext({ viewport: { width: 1440, height: 900 } });
+ const pD = await ctxD.newPage();
+ await pD.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
+ await pD.waitForTimeout(3500);
+
+ const titleInfo = await pD.evaluate(() => {
+ const out = [];
+ // find any element whose text is exactly the collection title
+ const candidates = [...document.querySelectorAll('h1,.page-title,.collection-title,.boost-sd__header-title,[class*="header-title"],[class*="page-title"]')];
+ for (const el of candidates) {
+ const t = (el.textContent || '').trim();
+ if (!t || t.length > 40) continue;
+ const cs = getComputedStyle(el);
+ const r = el.getBoundingClientRect();
+ out.push({
+ text: t, tag: el.tagName, cls: el.className,
+ bg: cs.backgroundColor, color: cs.color, radius: cs.borderRadius,
+ pad: cs.padding, display: cs.display,
+ w: Math.round(r.width), h: Math.round(r.height),
+ parentCls: el.parentElement ? el.parentElement.className : '',
+ parentBg: el.parentElement ? getComputedStyle(el.parentElement).backgroundColor : '',
+ });
+ }
+ // also the collection-header / boost header wrapper
+ const wraps = [];
+ for (const sel of ['.collection-header', '.boost-sd__collection-header', '.boost-sd__header-main', '.collection-header-content']) {
+ const el = document.querySelector(sel);
+ if (el) {
+ const cs = getComputedStyle(el); const r = el.getBoundingClientRect();
+ wraps.push({ sel, bg: cs.backgroundColor, w: Math.round(r.width), h: Math.round(r.height), radius: cs.borderRadius, pad: cs.padding });
+ }
+ }
+ // does a collection.image exist on the page?
+ const featured = document.querySelector('.collection-featured-image img, .boost-sd__header-image-inner img');
+ return { titles: out, wraps, hasFeaturedImage: !!featured, featuredSrc: featured ? featured.src : null };
+ });
+ console.log('=== DESKTOP TITLE / HEADER ===');
+ console.log(JSON.stringify(titleInfo, null, 2));
+ await ctxD.close();
+
+ // ---- MOBILE: measure the header gap ----
+ const iPhone = { viewport: { width: 390, height: 844 }, deviceScaleFactor: 3, isMobile: true, hasTouch: true,
+ userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1' };
+ const ctxM = await browser.newContext(iPhone);
+ const pM = await ctxM.newPage();
+ await pM.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
+ await pM.waitForTimeout(3500);
+
+ const headerInfo = await pM.evaluate(() => {
+ const rect = (sel) => { const el = document.querySelector(sel); if (!el) return null; const r = el.getBoundingClientRect(); const cs = getComputedStyle(el); return { sel, top: Math.round(r.top), bottom: Math.round(r.bottom), h: Math.round(r.height), pad: cs.padding, margin: cs.margin, minH: cs.minHeight, display: cs.display }; };
+ const sels = ['.header-tools-wrapper','.header-tools','.aligned-right','.mini-cart-wrapper','.cart-count','.header-main-content','.header-branding','.main-header','[data-section-header]'];
+ const measured = sels.map(rect).filter(Boolean);
+ // gap = top of branding minus bottom of cart-count
+ const cart = document.querySelector('.cart-count');
+ const brand = document.querySelector('.header-branding-desktop, .header-branding');
+ let gap = null;
+ if (cart && brand) gap = Math.round(brand.getBoundingClientRect().top - cart.getBoundingClientRect().bottom);
+ return { measured, cartToBrandGap: gap, windowH: window.innerHeight };
+ });
+ console.log('\n=== MOBILE HEADER ===');
+ console.log(JSON.stringify(headerInfo, null, 2));
+ await ctxM.close();
+
+ await browser.close();
+})();
diff --git a/shopify/collection-hero-fix/verify-collection-hero-fix.js b/shopify/collection-hero-fix/verify-collection-hero-fix.js
new file mode 100644
index 00000000..fddcddc1
--- /dev/null
+++ b/shopify/collection-hero-fix/verify-collection-hero-fix.js
@@ -0,0 +1,91 @@
+// Verify the staged collection-hero fix by applying the EXACT staged CSS + the
+// snippet's inline <style> output to the real live /collections/all DOM, both
+// viewports, BEFORE/AFTER screenshots. Read-only network; $0 local.
+const { chromium } = require('playwright');
+const fs = require('fs');
+
+const CSS = fs.readFileSync(__dirname + '/shopify/collection-hero-fix/dw-collection-hero.css', 'utf8');
+const URL = 'https://www.designerwallcoverings.com/collections/all';
+const SHOTS = '/tmp/dwshots';
+
+// Simulate the snippet output for the NO-IMAGE case (collection.image blank,
+// settings.collection_default_hero blank): the snippet emits only the wrapper
+// flag, no --dw-hero-img -> soft band + clean dark type. We also test the
+// IMAGE-PRESENT case by injecting a sample hero url.
+const SNIPPET_NOIMG = `
+ .boost-sd__collection-header,.template-collection .collection-header{--dw-hero-applied:1;}
+`;
+const SAMPLE_IMG = 'https://www.designerwallcoverings.com/cdn/shop/files/marble.jpg';
+const SNIPPET_IMG = `
+ body.dw-collection-has-hero .boost-sd__collection-header{
+ background-image:linear-gradient(180deg,rgba(0,0,0,.18),rgba(0,0,0,.34)),url("${SAMPLE_IMG}") !important;
+ background-size:cover !important;background-position:center !important;min-height:240px;
+ display:flex;align-items:center;justify-content:center;text-align:center;}
+ body.dw-collection-has-hero .boost-sd__collection-header .boost-sd__header-title{color:#fff !important;background:rgba(0,0,0,.42) !important;border-radius:4px !important;padding:12px 28px !important;display:inline-block !important;}
+`;
+
+async function probeTitle(page) {
+ return page.evaluate(() => {
+ const pick = (sel) => { const el = document.querySelector(sel); if (!el) return null; const cs = getComputedStyle(el); const r = el.getBoundingClientRect(); return { sel, bg: cs.backgroundColor, color: cs.color, radius: cs.borderRadius, w: Math.round(r.width), h: Math.round(r.height), visible: cs.display !== 'none' && r.height > 0 }; };
+ return {
+ boostTitle: pick('.boost-sd__header-title'),
+ themeTitle: pick('.page-title'),
+ boostHeader: pick('.boost-sd__collection-header'),
+ };
+ });
+}
+
+(async () => {
+ const browser = await chromium.launch();
+
+ for (const [name, vp, mobile] of [
+ ['desktop', { width: 1440, height: 900 }, false],
+ ['mobile', { width: 390, height: 844 }, true],
+ ]) {
+ const ctx = await browser.newContext({ viewport: vp, isMobile: mobile, deviceScaleFactor: mobile ? 3 : 1,
+ userAgent: mobile ? 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1' : undefined });
+ const page = await ctx.newPage();
+ await page.goto(URL, { waitUntil: 'domcontentloaded', timeout: 60000 });
+ await page.waitForTimeout(3500);
+
+ // BEFORE
+ await page.screenshot({ path: `${SHOTS}/dev-${name}-BEFORE.png`, fullPage: false });
+ const before = await probeTitle(page);
+
+ // APPLY staged CSS + snippet (no-image case = the real /collections/all state)
+ await page.addStyleTag({ content: CSS });
+ await page.addStyleTag({ content: SNIPPET_NOIMG });
+ // tag wrapper class the snippet/JS would add for has-image detection (none here)
+ await page.waitForTimeout(400);
+ await page.screenshot({ path: `${SHOTS}/dev-${name}-AFTER-noimage.png`, fullPage: false });
+ const afterNoImg = await probeTitle(page);
+
+ // Now simulate IMAGE-PRESENT case (snippet sets body.dw-collection-has-hero)
+ await page.addStyleTag({ content: SNIPPET_IMG });
+ await page.evaluate(() => { document.body.classList.add('dw-collection-has-hero'); document.documentElement.classList.add('dw-coll-hero'); });
+ await page.waitForTimeout(400);
+ await page.screenshot({ path: `${SHOTS}/dev-${name}-AFTER-withimage.png`, fullPage: false });
+ const afterImg = await probeTitle(page);
+
+ console.log(`\n=== ${name.toUpperCase()} ===`);
+ console.log('BEFORE boostTitle bg:', before.boostTitle && before.boostTitle.bg, '| themeTitle visible:', before.themeTitle && before.themeTitle.visible);
+ console.log('AFTER-noimg boostTitle bg:', afterNoImg.boostTitle && afterNoImg.boostTitle.bg, 'color:', afterNoImg.boostTitle && afterNoImg.boostTitle.color, '| themeTitle visible:', afterNoImg.themeTitle && afterNoImg.themeTitle.visible);
+ console.log('AFTER-img boostTitle bg:', afterImg.boostTitle && afterImg.boostTitle.bg, 'color:', afterImg.boostTitle && afterImg.boostTitle.color, '| header bg-image applied:', afterImg.boostHeader && afterImg.boostHeader.bg);
+
+ if (mobile) {
+ const gap = await page.evaluate(() => {
+ const cart = document.querySelector('.cart-count');
+ const brand = document.querySelector('.header-branding-desktop, .header-branding');
+ const g = (cart && brand) ? Math.round(brand.getBoundingClientRect().top - cart.getBoundingClientRect().bottom) : null;
+ const bH = brand ? Math.round(brand.getBoundingClientRect().height) : null;
+ const mh = document.querySelector('.main-header');
+ return { cartToBrandGap: g, brandingHeight: bH, headerHeight: mh ? Math.round(mh.getBoundingClientRect().height) : null };
+ });
+ console.log('MOBILE F3 after-fix:', JSON.stringify(gap), '(BEFORE was gap~11, brandingH 118, headerH 160)');
+ }
+
+ await ctx.close();
+ }
+ await browser.close();
+ console.log('\nScreenshots in', SHOTS);
+})();
← 06ab4e14 Designtex: comprehensive gated memo + live-77 UOM flip scrip
·
back to Designer Wallcoverings
·
Draft gated live-push memo + before/after screenshots for co a3d6742a →