[object Object]

← 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, '&quot;')}">
     <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)