[object Object]

← back to 1900swallpaper

homepage: render 4 tag-bucket rails (art nouveau/botanical/floral/arts & crafts) so single-aesthetic catalog gets meaningful segmentation

40159d2848ac640ed89bc35782a3dd1a28e58cd0 · 2026-05-26 08:10:02 -0700 · Steve

Files touched

Diff

commit 40159d2848ac640ed89bc35782a3dd1a28e58cd0
Author: Steve <steve@designerwallcoverings.com>
Date:   Tue May 26 08:10:02 2026 -0700

    homepage: render 4 tag-bucket rails (art nouveau/botanical/floral/arts & crafts) so single-aesthetic catalog gets meaningful segmentation
---
 public/index.html | 35 +++++++++++++++++++++++++++++++++++
 server.js         |  5 +++++
 2 files changed, 40 insertions(+)

diff --git a/public/index.html b/public/index.html
index 2abb669..e4cd142 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1067,6 +1067,41 @@ loadGridPage();
   });
 })();
 
+// SWEEP-A1: tag-bucket rails — additional Ideas rails sliced by tag
+window.IDEA_RAIL_TAGS = ["art nouveau","botanical","floral","arts & crafts"];
+(async function hydrateTagRails() {
+  try {
+    const host = document.getElementById('ideas');
+    if (!host || !Array.isArray(window.IDEA_RAIL_TAGS)) return;
+    function cardOne(p) {
+      const url = (p.image_url || '').replace(/(_\d+x\d*)(\.(jpg|jpeg|png|webp|gif))(\?|$)/i, '$2$4');
+      const title = ((p.title || '').split('|')[0] || '').trim().replace(/"/g, '&quot;');
+      return '<a class="rail-card" href="/sample/' + encodeURIComponent(p.handle || p.sku) + '">' +
+        '<img src="' + url + '" alt="' + title + '" loading="lazy">' +
+        '<div class="rc-title">' + title + '</div>' +
+        '<div class="rc-meta">Designer Wallcoverings</div>' +
+      '</a>';
+    }
+    for (const tag of window.IDEA_RAIL_TAGS) {
+      const r = await fetch('/api/products?limit=12&tag=' + encodeURIComponent(tag));
+      if (!r.ok) continue;
+      const d = await r.json();
+      const items = (d.items || []).filter(p => p && p.image_url);
+      if (items.length < 4) continue;
+      const block = document.createElement('div');
+      block.className = 'rail-block';
+      const niceTag = tag.replace(/\b\w/g, c => c.toUpperCase());
+      block.innerHTML =
+        '<div class="rail-eyebrow">Browse</div>' +
+        '<div style="display:flex;justify-content:space-between;align-items:baseline;margin-bottom:18px">' +
+          '<h2 class="rail-title" style="margin:0">' + niceTag + '</h2>' +
+          '<a href="/?tag=' + encodeURIComponent(tag) + '" style="font-size:11px;letter-spacing:.22em;text-transform:uppercase;color:var(--muted,#888);text-decoration:none;border-bottom:1px solid var(--line,rgba(255,255,255,.2));padding-bottom:2px">View all ' + (d.total || items.length) + ' &rarr;</a>' +
+        '</div>' +
+        '<div class="rail">' + items.map(cardOne).join('') + '</div>';
+      host.appendChild(block);
+    }
+  } catch (e) {}
+})();
 // graphics-loop pass 11: hydrate Ideas rails from /api/products
 (async function hydrateIdeas() {
   try {
diff --git a/server.js b/server.js
index 02f3891..ead859c 100644
--- a/server.js
+++ b/server.js
@@ -164,6 +164,11 @@ app.get('/api/products', (req, res) => {
     list = list.filter(p => (p.title || '').toLowerCase().includes(needle) || (p.tags || []).some(t => t.toLowerCase().includes(needle)));
   }
   if (aesthetic && aesthetic !== 'all') list = list.filter(p => p.aesthetic === aesthetic);
+  // SWEEP-A1: tag filter — sub-bucket rails on the homepage call /api/products?tag=<name>
+  if (req.query.tag) {
+    const wanted = String(req.query.tag).toLowerCase();
+    list = list.filter(p => (p.tags||[]).some(t => String(t).toLowerCase() === wanted));
+  }
   if (vendor && vendor !== 'all') list = list.filter(p => p.vendor === vendor);
   list = sortProducts(list, sort);
   const total = list.length;

← c936d1a wire api-vendor-redact middleware (close vendors-facet leak)  ·  back to 1900swallpaper  ·  rails: editorial labels for firing aesthetic buckets fd369c0 →