[object Object]

← back to Designer Wallcoverings

grassclothwallpaper: add density slider beside sort + corner-nav

1b25aa754dc348d75105803aab54ea2c019b0b29 · 2026-05-27 08:01:09 -0700 · Steve

Files touched

Diff

commit 1b25aa754dc348d75105803aab54ea2c019b0b29
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed May 27 08:01:09 2026 -0700

    grassclothwallpaper: add density slider beside sort + corner-nav
---
 DW-Websites/GrassclothWallpaper.com/corner-nav.js | 195 ++++++++++++++++++++++
 DW-Websites/GrassclothWallpaper.com/index.html    |  35 +++-
 2 files changed, 226 insertions(+), 4 deletions(-)

diff --git a/DW-Websites/GrassclothWallpaper.com/corner-nav.js b/DW-Websites/GrassclothWallpaper.com/corner-nav.js
new file mode 100644
index 00000000..2bda0fc7
--- /dev/null
+++ b/DW-Websites/GrassclothWallpaper.com/corner-nav.js
@@ -0,0 +1,195 @@
+/* corner-nav.js — universal upper-right 4-icon nav for the DW fleet.
+ *
+ * What it does: injects a fixed top-right widget with four icons —
+ *   1. Bag       → links to designerwallcoverings.com search for this site's keyword
+ *   2. Person    → opens trade-account panel (info@<domain> + Shopify /account link)
+ *   3. Magnifier → toggles a search overlay; filters [data-grid] if present, else /search
+ *   4. Menu      → toggles a drawer with the site's existing nav + sister-site links
+ *
+ * Self-styled. Idempotent. Drop-in:  <script src="/corner-nav.js" defer></script>
+ *
+ * Per-site overrides: set `window.CornerNavConfig = { ... }` BEFORE the script loads.
+ *   { bagUrl, accountUrl, searchSelector, menuLinks: [{name,url}], skipMenu: bool }
+ *
+ * Reference: Gucci / YSL top-right luxury nav pattern. Per Steve 2026-05-13.
+ */
+(function () {
+  'use strict';
+  if (window.__cornerNavLoaded) return;
+  window.__cornerNavLoaded = true;
+
+  var CFG = window.CornerNavConfig || {};
+  var DOMAIN = location.hostname.replace(/^www\./, '');
+  var STEM   = DOMAIN.replace(/\..+$/, '');
+  function titleCase(s) {
+    // 'corkwallcovering' → 'Cork Wallcovering'
+    // 'hospitalitywallpaper' → 'Hospitality Wallpaper'
+    return s
+      .replace(/(wallcovering|wallpaper|fabric|wallcoverings|walls|paper|cover|covering)/gi, ' $1')
+      .replace(/\s+/g, ' ')
+      .trim()
+      .replace(/\b\w/g, function (c) { return c.toUpperCase(); });
+  }
+  var rawBrand = (document.querySelector('meta[name="brand"]') || {}).content
+              || ((document.querySelector('.ns-name') || {}).textContent || '').trim()
+              || STEM;
+  // Title-case if the source is all-lowercase (the .ns-name spans on DW sites
+  // are intentionally lowercase for typography; the menu panel header wants
+  // proper case so it doesn't read as a slug).
+  var BRAND = (rawBrand === rawBrand.toLowerCase()) ? titleCase(rawBrand) : rawBrand;
+
+  var BAG_URL     = CFG.bagUrl     || 'https://designerwallcoverings.com/search?q=' + encodeURIComponent(BRAND);
+  var ACCOUNT_URL = CFG.accountUrl || 'https://designerwallcoverings.com/account';
+  var INFO_EMAIL  = CFG.infoEmail  || ('info@' + DOMAIN);
+
+  // ---------- styles (scoped via .cnav prefix) ----------
+  var css = '\
+.cnav-wrap{position:fixed;top:14px;right:18px;z-index:9000;display:flex;align-items:center;gap:18px;padding:10px 16px;background:rgba(10,10,10,0.78);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,0.08);border-radius:999px;font-family:-apple-system,"SF Pro Text",system-ui,sans-serif;color:#f5f1e8;font-size:13px;letter-spacing:0.04em;text-transform:uppercase}\
+.cnav-btn{background:none;border:0;cursor:pointer;color:inherit;padding:4px;display:flex;align-items:center;gap:8px;transition:opacity .2s}\
+.cnav-btn:hover{opacity:0.7}\
+.cnav-btn svg{width:22px;height:22px;stroke:currentColor;fill:none;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}\
+.cnav-btn .cnav-lbl{font-size:13px;font-weight:400;letter-spacing:0.06em}\
+.cnav-overlay{position:fixed;inset:0;z-index:8999;background:rgba(0,0,0,0.5);opacity:0;pointer-events:none;transition:opacity .2s}\
+.cnav-overlay.show{opacity:1;pointer-events:auto}\
+.cnav-panel{position:fixed;top:0;right:0;height:100vh;width:min(420px,92vw);background:#0a0a0a;color:#f5f1e8;z-index:9001;transform:translateX(100%);transition:transform .3s ease;padding:88px 32px 24px;overflow-y:auto;border-left:1px solid rgba(255,255,255,0.08);box-sizing:border-box}\
+.cnav-panel.show{transform:translateX(0)}\
+.cnav-panel h3{font-family:"Playfair Display",Georgia,serif;font-weight:400;font-size:20px;margin:0 0 16px;letter-spacing:0.04em}\
+.cnav-panel .cnav-close{position:absolute;top:18px;right:18px;background:none;border:0;color:#f5f1e8;font-size:28px;cursor:pointer;line-height:1;padding:6px}\
+.cnav-panel a, .cnav-panel button.cnav-link{display:block;padding:12px 0;color:#f5f1e8;text-decoration:none;border-bottom:1px solid rgba(255,255,255,0.08);font-size:15px;letter-spacing:0.02em;background:none;border-left:0;border-right:0;border-top:0;width:100%;text-align:left;cursor:pointer;font-family:inherit}\
+.cnav-panel a:hover, .cnav-panel button.cnav-link:hover{opacity:0.7}\
+.cnav-search{padding:14px 16px;width:100%;font-size:18px;background:transparent;border:0;border-bottom:1px solid rgba(255,255,255,0.18);color:#f5f1e8;outline:none;box-sizing:border-box;margin-bottom:18px;font-family:inherit}\
+.cnav-search::placeholder{color:rgba(245,241,232,0.4)}\
+.cnav-sub{font-size:12px;color:rgba(245,241,232,0.55);margin:18px 0 8px;letter-spacing:0.08em;text-transform:uppercase}\
+@media (max-width:520px){.cnav-wrap{top:10px;right:10px;padding:8px 12px;gap:12px}.cnav-btn .cnav-lbl{display:none}.cnav-btn svg{width:20px;height:20px}}\
+';
+  var styleEl = document.createElement('style');
+  styleEl.setAttribute('data-cnav', '1');
+  styleEl.appendChild(document.createTextNode(css));
+  document.head.appendChild(styleEl);
+
+  // ---------- icons ----------
+  var ICONS = {
+    bag:    '<svg viewBox="0 0 24 24"><path d="M6 7h12l-1 13H7L6 7Z"/><path d="M9 7V5a3 3 0 0 1 6 0v2"/></svg>',
+    person: '<svg viewBox="0 0 24 24"><circle cx="12" cy="8" r="3.2"/><path d="M5 20c0-3.5 3-6 7-6s7 2.5 7 6"/></svg>',
+    search: '<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="6"/><path d="m20 20-4.3-4.3"/></svg>',
+    menu:   '<svg viewBox="0 0 24 24"><path d="M4 7h16M4 12h16M4 17h16"/></svg>',
+  };
+
+  // ---------- widget ----------
+  var wrap = document.createElement('div');
+  wrap.className = 'cnav-wrap';
+  wrap.setAttribute('role', 'navigation');
+  wrap.setAttribute('aria-label', 'Primary');
+  wrap.innerHTML = '\
+    <a class="cnav-btn" href="' + BAG_URL + '" aria-label="Shop"     title="Shop">'    + ICONS.bag    + '</a>\
+    <button class="cnav-btn" id="cnav-account" aria-label="Account"  title="Account">' + ICONS.person + '</button>\
+    <button class="cnav-btn" id="cnav-search"  aria-label="Search"   title="Search">'  + ICONS.search + '</button>\
+    <button class="cnav-btn" id="cnav-menu"    aria-label="Menu"     title="Menu">'    + ICONS.menu   + '<span class="cnav-lbl">Menu</span></button>\
+  ';
+
+  var overlay = document.createElement('div');
+  overlay.className = 'cnav-overlay';
+
+  // ---------- panels ----------
+  function panel(html) {
+    var p = document.createElement('aside');
+    p.className = 'cnav-panel';
+    p.innerHTML = '<button class="cnav-close" aria-label="Close">×</button>' + html;
+    return p;
+  }
+  function openPanel(p) { overlay.classList.add('show'); p.classList.add('show'); }
+  function closeAll() {
+    overlay.classList.remove('show');
+    document.querySelectorAll('.cnav-panel.show').forEach(function (el) { el.classList.remove('show'); });
+  }
+
+  // Account panel
+  var acct = panel('\
+    <h3>Trade account</h3>\
+    <p class="cnav-sub" style="margin-top:0">Verified-buyer access to memo samples, trade pricing, and project quoting.</p>\
+    <a href="' + ACCOUNT_URL + '">Sign in / register →</a>\
+    <a href="mailto:' + INFO_EMAIL + '?subject=' + encodeURIComponent('Trade account inquiry') + '">Email ' + INFO_EMAIL + '</a>\
+    <a href="tel:+18883734564">Call (888) 373-4564 · M–F 9–5 PT</a>\
+  ');
+
+  // Search panel — filters [data-grid] if present, otherwise dispatches to /search
+  var hasGrid = document.querySelector('[data-grid]') || document.querySelector('.product-grid') || document.querySelector('#shop');
+  var srch = panel('\
+    <h3>Search</h3>\
+    <input type="search" class="cnav-search" id="cnav-search-input" placeholder="Search ' + BRAND + '..." autocomplete="off">\
+    <div class="cnav-sub">Press enter to search · ESC to close</div>\
+    <div id="cnav-search-results" style="margin-top:18px"></div>\
+  ');
+
+  // Menu panel — auto-extract existing site nav
+  var existingLinks = [];
+  document.querySelectorAll('.ns-nav a, header nav a, nav.primary a').forEach(function (a) {
+    var name = (a.textContent || '').trim();
+    var url  = a.getAttribute('href') || '#';
+    if (name && url && !existingLinks.some(function (e) { return e.name === name; })) {
+      existingLinks.push({ name: name, url: url });
+    }
+  });
+  if (!existingLinks.length) {
+    existingLinks = [
+      { name: 'Home',  url: '/' },
+      { name: 'About', url: '/about' },
+      { name: 'Shop',  url: BAG_URL },
+    ];
+  }
+  var menuLinks = (CFG.menuLinks && CFG.menuLinks.length) ? CFG.menuLinks : existingLinks;
+  var sisterLinks = [
+    { name: 'Designer Wallcoverings', url: 'https://designerwallcoverings.com' },
+    { name: 'Novasuede',              url: 'https://novasuede.com' },
+    { name: 'Phillipe Romano',        url: 'https://philipperomano.com' },
+    { name: 'Architectural Wallcoverings', url: 'https://architecturalwallcoverings.com' },
+  ];
+  var mnu = panel('\
+    <h3>' + BRAND + '</h3>\
+    ' + menuLinks.map(function (l) { return '<a href="' + l.url + '">' + l.name + '</a>'; }).join('') + '\
+    <div class="cnav-sub" style="margin-top:24px">Sister sites</div>\
+    ' + sisterLinks.filter(function (l) { return l.url.indexOf(DOMAIN) === -1; })
+                   .map(function (l) { return '<a href="' + l.url + '">' + l.name + '</a>'; }).join('') + '\
+  ');
+
+  document.body.appendChild(wrap);
+  document.body.appendChild(overlay);
+  document.body.appendChild(acct);
+  document.body.appendChild(srch);
+  document.body.appendChild(mnu);
+
+  // ---------- wiring ----------
+  document.getElementById('cnav-account').addEventListener('click', function (e) { e.preventDefault(); openPanel(acct); });
+  document.getElementById('cnav-search').addEventListener('click',  function (e) { e.preventDefault(); openPanel(srch); setTimeout(function () { document.getElementById('cnav-search-input').focus(); }, 320); });
+  document.getElementById('cnav-menu').addEventListener('click',    function (e) { e.preventDefault(); openPanel(mnu); });
+  overlay.addEventListener('click', closeAll);
+  document.querySelectorAll('.cnav-close').forEach(function (b) { b.addEventListener('click', closeAll); });
+  document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeAll(); });
+
+  // Search — live-filter on-page grid if one exists; else submit to /search
+  var input = document.getElementById('cnav-search-input');
+  var results = document.getElementById('cnav-search-results');
+  if (input) {
+    input.addEventListener('input', function () {
+      var q = input.value.trim().toLowerCase();
+      if (!hasGrid) return;
+      var cards = hasGrid.querySelectorAll('[data-card], .product-card, .pcard, .grid-item');
+      var shown = 0;
+      cards.forEach(function (c) {
+        var t = c.textContent.toLowerCase();
+        var hit = !q || t.indexOf(q) !== -1;
+        c.style.display = hit ? '' : 'none';
+        if (hit) shown++;
+      });
+      results.textContent = q ? (shown + ' result' + (shown === 1 ? '' : 's') + ' on this page') : '';
+    });
+    input.addEventListener('keydown', function (e) {
+      if (e.key === 'Enter') {
+        var q = input.value.trim();
+        if (!q) return;
+        if (hasGrid) { closeAll(); hasGrid.scrollIntoView({ behavior: 'smooth' }); }
+        else { location.href = '/search?q=' + encodeURIComponent(q); }
+      }
+    });
+  }
+})();
diff --git a/DW-Websites/GrassclothWallpaper.com/index.html b/DW-Websites/GrassclothWallpaper.com/index.html
index b69e2719..e2c17467 100644
--- a/DW-Websites/GrassclothWallpaper.com/index.html
+++ b/DW-Websites/GrassclothWallpaper.com/index.html
@@ -31,6 +31,10 @@
     <!-- Bing Site Verification -->
     <meta name="msvalidate.01" content="YOUR_BING_VERIFICATION_CODE">
     <style>
+        :root {
+            --cols: 4;
+        }
+
         * {
             margin: 0;
             padding: 0;
@@ -198,7 +202,7 @@
         /* Product Grid */
         .products-grid {
             display: grid;
-            grid-template-columns: repeat(4, 1fr);
+            grid-template-columns: repeat(var(--cols), 1fr);
             gap: 30px;
             max-width: 1400px;
             margin: 0 auto;
@@ -593,7 +597,7 @@
         /* Responsive */
         @media (max-width: 1200px) {
             .products-grid {
-                grid-template-columns: repeat(3, 1fr);
+                grid-template-columns: repeat(min(var(--cols), 3), 1fr);
                 gap: 25px;
             }
 
@@ -604,7 +608,7 @@
 
         @media (max-width: 900px) {
             .products-grid {
-                grid-template-columns: repeat(2, 1fr);
+                grid-template-columns: repeat(min(var(--cols), 2), 1fr);
                 gap: 20px;
             }
 
@@ -619,7 +623,7 @@
             }
 
             .products-grid {
-                grid-template-columns: repeat(2, 1fr);
+                grid-template-columns: repeat(min(var(--cols), 2), 1fr);
                 gap: 15px;
             }
 
@@ -656,6 +660,7 @@
             }
         }
     </style>
+    <script src="/corner-nav.js" defer></script>
 </head>
 <body>
     <!-- Header -->
@@ -697,6 +702,10 @@
                     <option value="hue">Color Family</option>
                     <option value="sku">SKU Number</option>
                 </select>
+                <span class="density-control" style="display: flex; align-items: center; gap: 8px; padding: 8px 15px; border: 1px solid #ddd; border-radius: 20px; background: white;">
+                    <input type="range" id="densitySlider" min="4" max="10" step="1" value="4" oninput="setDensity(this.value)" style="cursor: pointer; vertical-align: middle;">
+                    <span id="densityLabel" style="font-size: 13px; color: #666; min-width: 48px;">4 cols</span>
+                </span>
             </div>
         </div>
     </section>
@@ -933,6 +942,23 @@
             return (100 - hsl.l) * 100000 + hueOrder * 100 + variation;
         }
 
+        // Density slider — controls grid column count via --cols CSS var
+        function setDensity(n) {
+            n = Math.max(4, Math.min(10, parseInt(n, 10) || 4));
+            document.documentElement.style.setProperty('--cols', n);
+            const label = document.getElementById('densityLabel');
+            if (label) label.textContent = n + ' cols';
+            const slider = document.getElementById('densitySlider');
+            if (slider && slider.value != n) slider.value = n;
+            localStorage.setItem('gwp_density', n);
+        }
+
+        function restoreDensity() {
+            const saved = parseInt(localStorage.getItem('gwp_density'), 10);
+            const n = (saved >= 4 && saved <= 10) ? saved : 4;
+            setDensity(n);
+        }
+
         // Apply sorting based on user selection
         function applySorting() {
             const sortMethod = document.getElementById('sortSelect').value;
@@ -1572,6 +1598,7 @@
 
         // Initialize on page load
         document.addEventListener('DOMContentLoaded', () => {
+            restoreDensity();
             loadProducts();
             verifyCSSCropping();
         });

← ec49ab47 Cole & Son: add mandatory $4.25 Sample variant to 26 product  ·  back to Designer Wallcoverings  ·  Update Claude model IDs to claude-opus-4-8 (Opus 4.8 upgrade 6114227d →