[object Object]

← back to Dw Domain Fleet

feat(fleet pdp): theme1/theme2 toggle via ?theme=N query-param gate

453b000ad459d788956231ba90de008bb3e4288c · 2026-05-29 13:10:52 -0700 · Steve Abrams

Adds the same theme1/theme2 preview surface to all 44 dwf-* product pages
via the shared productPage() template. Since dw-domain-fleet has no admin
auth, gate is by URL query param (?theme=2 → flip to theme2, persisted
in localStorage 'wallco-page-theme'). Toggle UI is hidden until theme2
mode is active so default visitors see vanilla theme1.

Files touched

Diff

commit 453b000ad459d788956231ba90de008bb3e4288c
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Fri May 29 13:10:52 2026 -0700

    feat(fleet pdp): theme1/theme2 toggle via ?theme=N query-param gate
    
    Adds the same theme1/theme2 preview surface to all 44 dwf-* product pages
    via the shared productPage() template. Since dw-domain-fleet has no admin
    auth, gate is by URL query param (?theme=2 → flip to theme2, persisted
    in localStorage 'wallco-page-theme'). Toggle UI is hidden until theme2
    mode is active so default visitors see vanilla theme1.
---
 shared/render.js | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 71 insertions(+)

diff --git a/shared/render.js b/shared/render.js
index 29bad9c..1189663 100644
--- a/shared/render.js
+++ b/shared/render.js
@@ -628,6 +628,77 @@ function productPage(cfg, p, related) {
   return head(cfg, title, desc, '/product/' + encodeURIComponent(productSlug(p)), canonical)
     + `<script type="application/ld+json">${JSON.stringify(productLd).replace(/<\/script/gi,'<\\/script')}</script>`
     + jsonld(cfg) + chrome(cfg) + `
+<!-- Theme toggle (theme1 / theme2) — query-param-gated preview surface
+     (Steve 2026-05-29). dw-domain-fleet has no admin auth, so this is
+     security-by-obscurity: ?theme=2 in the URL flips to theme2 and persists
+     in localStorage. The toggle UI itself is hidden until theme2 mode is
+     active so default visitors never see it. Shares localStorage key
+     'wallco-page-theme' for cross-site continuity. -->
+<script>
+(function(){
+  var qs = new URLSearchParams(location.search);
+  var fromUrl = qs.get('theme');
+  var saved = null; try { saved = localStorage.getItem('wallco-page-theme'); } catch(e){}
+  var theme;
+  if (fromUrl === '2' || fromUrl === 'theme2') theme = 'theme2';
+  else if (fromUrl === '1' || fromUrl === 'theme1') theme = 'theme1';
+  else if (saved === 'theme2' || saved === 'theme1') theme = saved;
+  else theme = 'theme1';
+  document.documentElement.setAttribute('data-page-theme', theme);
+  if (fromUrl) { try { localStorage.setItem('wallco-page-theme', theme); } catch(e){} }
+})();
+</script>
+<style id="page-theme-css-fleet">
+  [data-page-theme="theme2"] body{
+    --bg:#ffffff !important; --ink:#0a0a0a !important;
+    --muted:#5a5a5a !important; --line:#e5e5e5 !important;
+    --card-bg:#fafafa !important; --accent:#c14a2e !important;
+    background:#fff !important; color:#0a0a0a !important;
+  }
+  [data-page-theme="theme2"] h1,
+  [data-page-theme="theme2"] h2,
+  [data-page-theme="theme2"] h3{ letter-spacing:-0.01em; font-weight:500; }
+  [data-page-theme="theme2"] .card{
+    background:#fafafa !important; border:1px solid #e5e5e5 !important; box-shadow:none !important;
+  }
+  #page-theme-toggle{ display:none; }
+  [data-page-theme="theme2"] #page-theme-toggle,
+  [data-page-theme="theme1"] #page-theme-toggle{ display:inline-flex; }
+  #page-theme-toggle{
+    position:fixed; top:96px; right:14px; z-index:90;
+    gap:0; align-items:center;
+    background:rgba(255,255,255,.92); border:1px solid #d8d2c5;
+    border-radius:999px; padding:3px; backdrop-filter:blur(6px);
+    box-shadow:0 2px 10px rgba(0,0,0,.08);
+    font:600 11px ui-sans-serif,system-ui; letter-spacing:.06em; text-transform:uppercase;
+  }
+  #page-theme-toggle button{
+    border:0; background:transparent; color:#5a5048; cursor:pointer;
+    padding:6px 14px; border-radius:999px; font:inherit; transition:all .15s;
+  }
+  #page-theme-toggle button[aria-pressed="true"]{ background:#1a1714; color:#faf7f2; }
+  #page-theme-toggle button:hover:not([aria-pressed="true"]){ color:#1a1714; }
+</style>
+<div id="page-theme-toggle" role="group" aria-label="Page theme">
+  <button type="button" data-theme="theme1" aria-pressed="false">Theme 1</button>
+  <button type="button" data-theme="theme2" aria-pressed="false">Theme 2</button>
+</div>
+<script>
+(function(){
+  var cur = document.documentElement.getAttribute('data-page-theme') || 'theme1';
+  var btns = document.querySelectorAll('#page-theme-toggle button');
+  function sync(t){ btns.forEach(function(b){ b.setAttribute('aria-pressed', b.dataset.theme === t ? 'true' : 'false'); }); }
+  sync(cur);
+  btns.forEach(function(b){
+    b.addEventListener('click', function(){
+      var t = b.dataset.theme;
+      document.documentElement.setAttribute('data-page-theme', t);
+      try { localStorage.setItem('wallco-page-theme', t); } catch(e){}
+      sync(t);
+    });
+  });
+})();
+</script>
 <div style="height:84px"></div>
 <div class="wrap">
   <div style="display:grid;grid-template-columns:1.05fr .95fr;gap:48px;padding:28px 0 10px"

← 661cbec shared/render.js: scrub head-baked vendor from product title  ·  back to Dw Domain Fleet  ·  fix(fleet pdp): toggle hidden until ?theme=N opt-in 34fbe0e →