[object Object]

← back to Analytics Chips

index: add density slider + localStorage hydration for sort/filter/density

d173ece24e8aaa2c720689ba0e431d4818dab0d0 · 2026-05-19 17:27:21 -0700 · SteveStudio2

Standing-rule fix: any grid-rendering page needs sort + density controls
persisted to localStorage and hydrated before the first render.
categories.html already had this; index.html only had sort with no persistence.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit d173ece24e8aaa2c720689ba0e431d4818dab0d0
Author: SteveStudio2 <steve@designerwallcoverings.com>
Date:   Tue May 19 17:27:21 2026 -0700

    index: add density slider + localStorage hydration for sort/filter/density
    
    Standing-rule fix: any grid-rendering page needs sort + density controls
    persisted to localStorage and hydrated before the first render.
    categories.html already had this; index.html only had sort with no persistence.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
 public/index.html | 36 +++++++++++++++++++++++++++++++-----
 1 file changed, 31 insertions(+), 5 deletions(-)

diff --git a/public/index.html b/public/index.html
index a867fbf..5a6abcb 100644
--- a/public/index.html
+++ b/public/index.html
@@ -12,6 +12,7 @@
     --rule: #2a2a2e; --gold: #d4a04a; --good: #8fb89a; --warn: #d8b04a; --bad: #c87a6e;
     --serif: ui-serif, Georgia, "Times New Roman", serif;
     --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
+    --card-min: 240px;
   }
   * { box-sizing: border-box; }
   html, body { margin: 0; padding: 0; background: var(--bg); color: var(--ink); font: 400 14px/1.45 -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif; min-height: 100vh; }
@@ -35,7 +36,9 @@
 
   main { padding: 22px 26px 60px; max-width: 1600px; margin: 0 auto; }
 
-  .chips { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 10px; }
+  .chips { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--card-min), 1fr)); gap: 10px; }
+  header .controls .density { display: flex; align-items: center; gap: 6px; font-family: var(--mono); font-size: 10px; color: var(--ink-mute); letter-spacing: 0.10em; text-transform: uppercase; }
+  header .controls input[type=range] { width: 110px; cursor: pointer; }
   .chip {
     background: var(--bg-2); border: 1px solid var(--rule); border-radius: 4px;
     padding: 12px 14px 14px; transition: transform 120ms ease, border-color 120ms ease, background 120ms ease;
@@ -73,6 +76,7 @@
       <option value="sessions">Sessions (7d)</option>
       <option value="template">Template</option>
     </select>
+    <span class="density">density <input id="density" type="range" min="160" max="380" step="20"></span>
     <button id="refresh">Refresh</button>
   </div>
   <div class="meta">
@@ -94,8 +98,16 @@ const esc = (s) => String(s == null ? '' : s).replace(/&/g,'&amp;').replace(/</g
 
 let PROPERTIES = [];
 let METRICS = {};
-let SORT = 'alpha';
-let FILTER = '';
+// Hydrate persisted controls BEFORE first render so the grid doesn't flash defaults.
+let SORT = localStorage.getItem('chips.sort') || 'alpha';
+let FILTER = localStorage.getItem('chips.filter') || '';
+
+function applyDensity(px) {
+  document.documentElement.style.setProperty('--card-min', px + 'px');
+  const el = document.getElementById('density');
+  if (el) el.value = px;
+  localStorage.setItem('chips.density', px);
+}
 
 function fmtNum(n) {
   if (n == null) return '—';
@@ -193,10 +205,24 @@ async function loadMetrics() {
   }
 }
 
-$('#filter').addEventListener('input', e => { FILTER = e.target.value; applyFilterSort(); });
-$('#sort').addEventListener('change', e => { SORT = e.target.value; applyFilterSort(); });
+$('#filter').addEventListener('input', e => {
+  FILTER = e.target.value;
+  localStorage.setItem('chips.filter', FILTER);
+  applyFilterSort();
+});
+$('#sort').addEventListener('change', e => {
+  SORT = e.target.value;
+  localStorage.setItem('chips.sort', SORT);
+  applyFilterSort();
+});
+$('#density').addEventListener('input', e => applyDensity(parseInt(e.target.value, 10)));
 $('#refresh').addEventListener('click', loadMetrics);
 
+// Reflect hydrated values in the controls before kicking off the first load.
+$('#filter').value = FILTER;
+$('#sort').value = SORT;
+applyDensity(parseInt(localStorage.getItem('chips.density') || '240', 10));
+
 loadProperties();
 </script>
 </body></html>

← 331b54b categories: harden external chip links with rel="noopener no  ·  back to Analytics Chips  ·  (newest)