← back to Silverleafwallpaper
rails: wire /api/sliders into horizontal scroll-snap rail sections [rails-degenerate]
fd0827e02a7bb2bbeccecd379ca78b76b43603ca · 2026-05-26 08:12:08 -0700 · Steve Abrams
Files touched
Diff
commit fd0827e02a7bb2bbeccecd379ca78b76b43603ca
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Tue May 26 08:12:08 2026 -0700
rails: wire /api/sliders into horizontal scroll-snap rail sections [rails-degenerate]
---
public/index.html | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 102 insertions(+)
diff --git a/public/index.html b/public/index.html
index d6b34f8..7fd0858 100644
--- a/public/index.html
+++ b/public/index.html
@@ -189,6 +189,20 @@ textarea:focus-visible,
outline: 2px solid var(--accent);
outline-offset: 2px;
}
+
+/* === RAILS PATCH CSS (DW fleet sweep A4 2026-05-26) === */
+#rails{display:block;margin:0 0 40px}
+.rail-section{margin-bottom:48px}
+.rail-head{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:14px;padding-bottom:10px;border-bottom:1px solid var(--line,rgba(0,0,0,.12))}
+.rail-title{font-family:inherit;font-size:18px;letter-spacing:.16em;text-transform:uppercase;color:var(--accent,#9A6E3A);font-weight:600;margin:0}
+.rail-all{font-family:inherit;font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:var(--muted,#5b5042);font-weight:500;transition:color .15s;cursor:pointer}
+.rail-all:hover{color:var(--accent,#9A6E3A);text-decoration:none}
+.grid.rail-grid{display:flex;gap:14px;overflow-x:auto;scroll-snap-type:x mandatory;padding-bottom:8px;scrollbar-width:none}
+.grid.rail-grid::-webkit-scrollbar{display:none}
+.grid.rail-grid > .card{flex:0 0 240px;scroll-snap-align:start}
+@media (max-width:720px){.grid.rail-grid > .card{flex:0 0 160px}}
+/* === END RAILS PATCH CSS === */
+
</style>
<script>
(function(){ try { var t = localStorage.getItem('sl_theme') || 'light'; document.documentElement.dataset.theme = t; } catch(e){} })();
@@ -394,6 +408,7 @@ textarea:focus-visible,
<button data-sort-col="aesthetic" class="col-aes">Aesthetic <span class="arr"></span></button>
<button data-sort-col="price">Price <span class="arr"></span></button>
</div>
+ <div id="rails" style="display:none"></div>
<div class="grid" id="grid"></div>
<div class="loading" id="loading">Loading more…</div>
<div class="sentinel" id="sentinel"></div>
@@ -625,6 +640,7 @@ function resetGrid() {
document.getElementById('grid').innerHTML = '';
document.getElementById('loading').textContent = 'Loading…';
loadGridPage();
+ if (typeof renderRailSections === 'function') renderRailSections();
}
async function loadGridPage() {
@@ -748,8 +764,94 @@ function setTheme(t){ document.documentElement.dataset.theme = t; try { localSto
setTheme(document.documentElement.dataset.theme || 'light');
tb.addEventListener('click', () => setTheme(document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark'));
+// === RAILS PATCH (DW fleet sweep A4 2026-05-26) ===
+// Renders rail sections on the unfiltered homepage above the flat grid.
+// Fed by /api/sliders. Hides flat grid + sentinel while rails are visible.
+async function renderRailSections(){
+ const railsHost = document.getElementById('rails');
+ const grid = document.getElementById('grid');
+ const sentinel = document.getElementById('sentinel');
+ const loading = document.getElementById('loading');
+ const listHead = document.getElementById('listHead');
+ if (!railsHost) return;
+ // Only render rails on the unfiltered "all" home view.
+ if (state.q || state.facet !== 'all') {
+ railsHost.innerHTML = '';
+ railsHost.style.display = 'none';
+ if (grid) grid.style.display = '';
+ if (sentinel) sentinel.style.display = '';
+ if (loading) loading.style.display = '';
+ return;
+ }
+ let payload;
+ try {
+ const r = await fetch('/api/sliders');
+ if (!r.ok) throw new Error('HTTP ' + r.status);
+ payload = await r.json();
+ } catch (e) {
+ console.warn('renderRailSections failed:', e);
+ return;
+ }
+ const rails = Array.isArray(payload && payload.rails) ? payload.rails : [];
+ if (rails.length < 2) return; // no meaningful segmentation
+ const frag = document.createElement('div');
+ for (const rail of rails) {
+ const aes = rail.aesthetic;
+ const items = (rail.items || []).slice(0, 8);
+ if (items.length < 4) continue;
+ const sec = document.createElement('section');
+ sec.className = 'rail-section';
+ const head = document.createElement('div');
+ head.className = 'rail-head';
+ const title = document.createElement('h2');
+ title.className = 'rail-title';
+ title.textContent = ((typeof LABELS !== 'undefined' && LABELS[aes]) || aes).replace(/-/g,' ').replace(/\b\w/g, c => c.toUpperCase());
+ const all = document.createElement('a');
+ all.className = 'rail-all';
+ all.href = '#shop';
+ all.textContent = 'View all →';
+ all.dataset.facet = aes;
+ all.onclick = (e) => {
+ e.preventDefault();
+ state.facet = aes;
+ document.querySelectorAll('#facets button').forEach(x => x.classList.toggle('active', x.dataset.facet === aes));
+ resetGrid();
+ const shop = document.getElementById('shop');
+ if (shop) shop.scrollIntoView({behavior:'smooth'});
+ };
+ head.appendChild(title); head.appendChild(all);
+ const grid8 = document.createElement('div');
+ grid8.className = 'grid rail-grid';
+ for (const p of items) {
+ const card = document.createElement('a');
+ card.className = 'card';
+ card.href = '#';
+ card.onclick = (e) => { e.preventDefault(); openDetails(p); };
+ card.innerHTML = cardHTML(p);
+ grid8.appendChild(card);
+ }
+ sec.appendChild(head);
+ sec.appendChild(grid8);
+ frag.appendChild(sec);
+ }
+ if (!frag.childNodes.length) return;
+ railsHost.innerHTML = '';
+ railsHost.appendChild(frag);
+ railsHost.style.display = '';
+ // Hide flat grid + scroll bait while rails are showing — clicking a rail
+ // "View all →" or a chip will un-hide them via resetGrid path.
+ if (grid) grid.style.display = 'none';
+ if (sentinel) sentinel.style.display = 'none';
+ if (loading) loading.style.display = 'none';
+ if (listHead) listHead.style.display = 'none';
+ const stat = document.getElementById('statLine');
+ if (stat) stat.textContent = (state.total || '') + ' patterns · browse by aesthetic';
+}
+// === END RAILS PATCH ===
+
loadFacets();
loadGridPage();
+renderRailSections();
</script>
<!-- ============================================================
DW UNIVERSAL CONTACT MODULE — fashion-house UX
← de0354f wire api-vendor-redact middleware — strip vendor field + ven
·
back to Silverleafwallpaper
·
rails: editorial labels for firing aesthetic buckets 039c059 →