← back to Dw Domain Fleet
Add sort <select> + density control bar to dwf homepage Featured grid (standing-rule compliance, client-side sort, shared localStorage with /catalog)
49997ea9dbd8089addcb9b4f9ccfb0143aa7cd16 · 2026-05-31 08:42:46 -0700 · steve
Files touched
Diff
commit 49997ea9dbd8089addcb9b4f9ccfb0143aa7cd16
Author: steve <steve@designerwallcoverings.com>
Date: Sun May 31 08:42:46 2026 -0700
Add sort <select> + density control bar to dwf homepage Featured grid (standing-rule compliance, client-side sort, shared localStorage with /catalog)
---
shared/render.js | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 66 insertions(+), 3 deletions(-)
diff --git a/shared/render.js b/shared/render.js
index d8d02c1..58ea85e 100644
--- a/shared/render.js
+++ b/shared/render.js
@@ -352,7 +352,7 @@ function card(p) {
return `<div class="card" data-handle="${esc(productSlug(p))}"
data-title="${esc(t)}" data-sku="${esc(scrubVendor(p.sku||p.handle))}"
data-img="${esc(p.image_url)}" data-tags="${esc(scrubTags(p.tags).slice(0,12).join(', '))}"
- data-price="${esc(p.price)}">
+ data-price="${esc(p.price)}" data-hex="${esc(hx)}">
<a class="card-link" href="${esc(href)}" aria-label="${esc(t)}">
<div class="card-img" style="background-image:url('${esc(p.image_url)}');--card-bg:url('${esc(p.image_url)}')"></div>
<div class="card-body">
@@ -385,8 +385,24 @@ function homePage(cfg, heroImgs, featured) {
<p>${esc(clean(cfg.tagline))} · Curated by Designer Wallcoverings</p>
</div>
<div class="wrap">
- <div class="controls"><label>Featured Selection</label><span class="count">${featured.length} patterns</span></div>
- <div class="grid">${featured.map(card).join('')}</div>
+ <div class="controls">
+ <div class="ctl-sort">
+ <label for="sortSel">Featured Selection · Sort</label>
+ <select id="sortSel">
+ <option value="newest">Featured</option><option value="color">Color</option>
+ <option value="light-dark">Light → Dark</option><option value="dark-light">Dark → Light</option>
+ <option value="style">Style</option><option value="sku">SKU A→Z</option>
+ <option value="title">Title A→Z</option><option value="price-asc">Price ↑</option>
+ <option value="price-desc">Price ↓</option>
+ </select>
+ </div>
+ <div class="ctl-density">
+ <label for="densSel">Density</label>
+ <input type="range" id="densSel" min="3" max="8" value="5">
+ <span class="count">${featured.length} patterns</span>
+ </div>
+ </div>
+ <div class="grid" id="grid">${featured.map(card).join('')}</div>
<div style="text-align:center;padding:10px 0 50px">
<a class="hero-cta__btn" style="position:static;background:var(--accent);color:#fff" href="/catalog">View Full Catalog</a>
</div>
@@ -394,10 +410,57 @@ function homePage(cfg, heroImgs, featured) {
${footer(cfg)}
${nsModal(cfg)}
${script(cfg)}
+${gridControls(cfg)}
${heroRotate(slides.length)}
</body></html>`;
}
+/* client-side sort + density for the homepage Featured grid (catalog does it
+ server-side; the featured set is small + server-rendered, so reorder in JS).
+ Shares localStorage keys with /catalog so the shopper's choice carries over. */
+function gridControls(cfg){
+ return `<script>
+(function(){
+ var dk='${cfg.slug}_density',sk='${cfg.slug}_sort';
+ var g=document.getElementById('grid'),d=document.getElementById('densSel'),s=document.getElementById('sortSel');
+ if(!g||!d||!s)return;
+ var cards=Array.prototype.slice.call(g.children);
+ cards.forEach(function(c,i){c.__i=i;}); // original "Featured" order
+ var sv=localStorage.getItem(dk);if(sv)d.value=sv;
+ function applyD(){g.style.setProperty('--cols',d.value);}
+ d.addEventListener('input',function(){applyD();localStorage.setItem(dk,d.value);});
+ applyD();
+ function hl(hex){var m=/([0-9a-f]{6})/i.exec(hex||'');if(!m)return[999,999];
+ var n=parseInt(m[1],16),r=(n>>16&255)/255,gg=(n>>8&255)/255,b=(n&255)/255;
+ var mx=Math.max(r,gg,b),mn=Math.min(r,gg,b),l=(mx+mn)/2,h=0,df=mx-mn;
+ if(df){if(mx==r)h=((gg-b)/df)%6;else if(mx==gg)h=(b-r)/df+2;else h=(r-gg)/df+4;h*=60;if(h<0)h+=360;}
+ return[h,l];}
+ function val(c,k){var x=c.dataset;
+ if(k==='title')return(x.title||'').toLowerCase();
+ if(k==='sku')return(x.sku||'').toLowerCase();
+ if(k==='style')return(x.tags||'').toLowerCase();
+ if(k==='price-asc'||k==='price-desc')return parseFloat(x.price)||0;
+ return hl(x.hex);}
+ function sortBy(k){
+ var arr=cards.slice();
+ arr.sort(function(a,b){
+ if(k==='newest')return a.__i-b.__i;
+ if(k==='price-asc')return val(a,k)-val(b,k);
+ if(k==='price-desc')return val(b,k)-val(a,k);
+ if(k==='color')return val(a,'color')[0]-val(b,'color')[0];
+ if(k==='light-dark')return val(b,'color')[1]-val(a,'color')[1];
+ if(k==='dark-light')return val(a,'color')[1]-val(b,'color')[1];
+ var av=val(a,k),bv=val(b,k);return av<bv?-1:av>bv?1:0;
+ });
+ arr.forEach(function(c){g.appendChild(c);});
+ }
+ var saved=localStorage.getItem(sk);
+ if(saved){var ok=Array.prototype.some.call(s.options,function(o){return o.value===saved;});if(ok){s.value=saved;sortBy(saved);}}
+ s.addEventListener('change',function(){localStorage.setItem(sk,s.value);sortBy(s.value);});
+})();
+</script>`;
+}
+
/* ---------- catalog page ---------- */
function catalogPage(cfg, products, q, sort, page, pages, total) {
const title = `Catalog — ${cfg.siteName}`;
← 55a7f0b Add 2026-05-31 read-only vendor-leak re-verify: slug/chip sc
·
back to Dw Domain Fleet
·
render: route PDP buy CTA + main-store anchor + canonical th a85eb02 →