← back to Bleachresistantfabrics
viewer standard: add Grid/List toggle + per-field on/off menu (Fields) to product grid
f07aed5247ae760f6a294c61f5a93f4baae8afd6 · 2026-08-12 08:33:42 -0700 · Steve
Files touched
Diff
commit f07aed5247ae760f6a294c61f5a93f4baae8afd6
Author: Steve <steve@designerwallcoverings.com>
Date: Wed Aug 12 08:33:42 2026 -0700
viewer standard: add Grid/List toggle + per-field on/off menu (Fields) to product grid
---
public/index.html | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 83 insertions(+), 2 deletions(-)
diff --git a/public/index.html b/public/index.html
index 8219bbf..487ffdc 100644
--- a/public/index.html
+++ b/public/index.html
@@ -153,6 +153,24 @@ footer { padding:64px 28px 28px; border-top:1px solid var(--line); margin-top:64
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible { outline:2px solid var(--accent); outline-offset:2px }
+/* ── Grid / List view ── */
+html.brf-list .grid { display:block; }
+html.brf-list .card { display:flex; flex-direction:row; align-items:stretch; aspect-ratio:auto; margin-bottom:10px; border-radius:0; }
+html.brf-list .card img { flex:0 0 clamp(80px,16vw,160px); width:clamp(80px,16vw,160px); height:auto; min-height:80px; object-fit:cover; }
+html.brf-list .card .overlay { position:static; transform:none; opacity:1; background:none; flex:1; display:flex; flex-direction:column; justify-content:center; padding:12px 14px; }
+html.brf-list .card .pat { color:var(--paper); }
+html.brf-list .card:hover { transform:none; }
+
+/* ── Per-field hide rules (html.hf-<field>) ── */
+html.hf-image .card img { display:none; }
+html.hf-title [data-f="title"] { display:none; }
+html.hf-sample [data-f="sample"] { display:none; }
+
+/* ── Fields popover labels ── */
+#brfFpop label { display:flex; align-items:center; gap:10px; padding:7px 14px; font-size:10px; letter-spacing:.14em; text-transform:uppercase; color:var(--paper); cursor:pointer; white-space:nowrap; font-family:inherit; }
+#brfFpop label:hover { background:var(--line); }
+#brfFpop input[type=checkbox] { accent-color:var(--accent); width:13px; height:13px; cursor:pointer; }
+
.faq { max-width:920px; margin:0 auto; padding:0 4px }
.faq details { border-bottom:1px solid var(--line); padding:0 }
.faq details:first-of-type { border-top:1px solid var(--line) }
@@ -329,6 +347,16 @@ a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible
<input id="cols" type="range" min="3" max="8" step="1" value="5">
<span class="ct" id="colsv">5 cols</span>
</div>
+ <!-- Grid/List view toggle -->
+ <div style="display:inline-flex;border:1px solid var(--line);border-radius:3px;overflow:hidden;" role="group" aria-label="View mode">
+ <button id="vGrid" style="padding:6px 12px;background:var(--accent);border:0;font-size:10px;letter-spacing:.18em;text-transform:uppercase;cursor:pointer;color:#0F1A1F;font-family:inherit;" title="Grid view">Grid</button>
+ <button id="vList" style="padding:6px 12px;background:transparent;border:0;font-size:10px;letter-spacing:.18em;text-transform:uppercase;cursor:pointer;color:var(--muted);font-family:inherit;" title="List view">List</button>
+ </div>
+ <!-- Fields on/off menu -->
+ <div class="brf-fieldmenu" style="position:relative;">
+ <button id="brfFieldsBtn" style="padding:7px 12px;border:1px solid var(--line);background:transparent;font-size:10px;letter-spacing:.18em;text-transform:uppercase;color:var(--muted);cursor:pointer;font-family:inherit;" aria-haspopup="true" aria-expanded="false">Fields ▾</button>
+ <div id="brfFpop" hidden style="position:absolute;top:calc(100% + 8px);right:0;background:var(--bg-soft);border:1px solid var(--line);box-shadow:0 12px 30px rgba(0,0,0,.35);padding:6px 4px;z-index:40;min-width:176px;" role="menu"></div>
+ </div>
</div>
<div class="stat-line" id="stat">Loading catalog…</div>
@@ -547,9 +575,9 @@ function card(p) {
return `<a class="card" href="${url}" target="_blank" rel="noopener noreferrer">
<img loading="lazy" src="${p.image_url}" alt="${(p.title || '').replace(/"/g, '"')}">
<div class="overlay">
- <div class="pat">${p.title || ''}</div>
+ <div class="pat" data-f="title">${p.title || ''}</div>
<div class="actions">
- <span class="sample-btn">Free Memo</span>
+ <span class="sample-btn" data-f="sample">Free Memo</span>
</div>
</div>
</a>`;
@@ -599,6 +627,59 @@ io.observe($more);
// applyFilters('none') paints the chips/sort/search + loads without a new history push.
readURL();
applyFilters('none');
+
+// ── Grid/List view toggle ──────────────────────────────────────────────────
+const BRF_FIELDS = [
+ ['image', 'Image', true],
+ ['title', 'Title', true],
+ ['sample', 'Sample', true],
+];
+function brfLoadFields(){
+ const def = {}; BRF_FIELDS.forEach(([k,,d]) => def[k] = d);
+ try { return Object.assign(def, JSON.parse(localStorage.getItem('brf_fields') || '{}')); } catch(e){ return def; }
+}
+const brfFieldState = brfLoadFields();
+
+function brfApplyView(){
+ const v = localStorage.getItem('brf_view') || 'grid';
+ document.documentElement.classList.toggle('brf-list', v === 'list');
+ document.getElementById('vGrid').style.background = v === 'grid' ? 'var(--accent)' : 'transparent';
+ document.getElementById('vGrid').style.color = v === 'grid' ? '#0F1A1F' : 'var(--muted)';
+ document.getElementById('vList').style.background = v === 'list' ? 'var(--accent)' : 'transparent';
+ document.getElementById('vList').style.color = v === 'list' ? '#0F1A1F' : 'var(--muted)';
+}
+document.getElementById('vGrid').addEventListener('click', () => { localStorage.setItem('brf_view', 'grid'); brfApplyView(); });
+document.getElementById('vList').addEventListener('click', () => { localStorage.setItem('brf_view', 'list'); brfApplyView(); });
+brfApplyView();
+
+// ── Fields ▾ menu ─────────────────────────────────────────────────────────
+function brfApplyFields(){
+ BRF_FIELDS.forEach(([k]) => document.documentElement.classList.toggle('hf-' + k, !brfFieldState[k]));
+}
+function brfBuildFieldMenu(){
+ const pop = document.getElementById('brfFpop');
+ pop.innerHTML = BRF_FIELDS.map(([k, label]) =>
+ `<label><input type="checkbox" data-field="${k}"${brfFieldState[k] ? ' checked' : ''}> ${label}</label>`).join('');
+ pop.querySelectorAll('input').forEach(cb => cb.addEventListener('change', () => {
+ brfFieldState[cb.dataset.field] = cb.checked;
+ localStorage.setItem('brf_fields', JSON.stringify(brfFieldState));
+ brfApplyFields();
+ }));
+}
+const brfFieldsBtn = document.getElementById('brfFieldsBtn');
+const brfFpop = document.getElementById('brfFpop');
+brfFieldsBtn.addEventListener('click', e => {
+ e.stopPropagation();
+ const open = brfFpop.hidden; brfFpop.hidden = !open;
+ brfFieldsBtn.setAttribute('aria-expanded', String(open));
+});
+document.addEventListener('click', e => {
+ if (!brfFpop.hidden && !e.target.closest('.brf-fieldmenu')) {
+ brfFpop.hidden = true; brfFieldsBtn.setAttribute('aria-expanded', 'false');
+ }
+});
+brfBuildFieldMenu();
+brfApplyFields();
</script>
← 72bd90c Wire DW Meta Pixel 1431180262113856 + DW-network ad slot [ad
·
back to Bleachresistantfabrics
·
(newest)