[object Object]

← back to Wallpaperdefinitions

add sort + density controls to definitions grid (Curated/A-Z/Z-A/Category + 1-3 col slider, localStorage-persisted)

ae7164e9b822beea09d564992b139ee366affcba · 2026-05-30 21:08:17 -0700 · SteveStudio2

Files touched

Diff

commit ae7164e9b822beea09d564992b139ee366affcba
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Sat May 30 21:08:17 2026 -0700

    add sort + density controls to definitions grid (Curated/A-Z/Z-A/Category + 1-3 col slider, localStorage-persisted)
---
 public/index.html | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 113 insertions(+), 4 deletions(-)

diff --git a/public/index.html b/public/index.html
index 570cf16..eafb7f0 100644
--- a/public/index.html
+++ b/public/index.html
@@ -164,19 +164,48 @@ body {
 }
 .intro-text { font-size: 1.05rem; color: var(--ink-soft); max-width: 680px; line-height: 1.85; }
 
+/* ===== DEFINITION CONTROLS (sort + density) ===== */
+.def-controls {
+  display: flex; flex-wrap: wrap; align-items: center; gap: 1.5rem;
+  margin: 0 0 2rem; padding: 1rem 1.2rem;
+  background: var(--card-bg);
+  border: 1px solid var(--line);
+  border-radius: 10px;
+}
+.def-control { display: flex; align-items: center; gap: 0.6rem; }
+.def-control label {
+  font-size: 0.7rem; letter-spacing: 0.16em; text-transform: uppercase;
+  color: var(--ink-faint); white-space: nowrap;
+}
+.def-control select {
+  background: var(--bg); color: var(--ink);
+  border: 1px solid var(--line); border-radius: 6px;
+  padding: 0.45rem 0.7rem; font-size: 0.85rem;
+  font-family: inherit; cursor: pointer;
+}
+.def-control select:focus { outline: none; border-color: var(--accent); }
+.def-control.density { flex: 1; min-width: 180px; }
+.def-control.density input[type="range"] {
+  flex: 1; accent-color: var(--accent); cursor: pointer; min-width: 90px;
+}
+
 /* ===== CARD STACK (scroll-snap) ===== */
 .card-stack {
   display: grid;
-  grid-template-columns: 1fr;
-  gap: 0;
+  grid-template-columns: repeat(var(--cols, 1), minmax(0, 1fr));
+  gap: 1rem;
+  align-items: start;
 }
+.card-stack .image-break { grid-column: 1 / -1; margin: 1.5rem 0; }
+/* multi-column: collapse each card's inner term|body layout to a single column */
+.card-stack.cols-multi .def-card { grid-template-columns: 1fr; gap: 0.7rem; padding: 1.5rem 1.6rem; }
 .def-card {
   scroll-snap-align: start;
   background: var(--card-bg);
   border: 1px solid var(--line);
   border-radius: 12px;
   padding: 2rem 2.2rem;
-  margin-bottom: 1rem;
+  margin-bottom: 0;
   backdrop-filter: blur(20px) saturate(180%);
   -webkit-backdrop-filter: blur(20px) saturate(180%);
   transition: transform 0.25s, box-shadow 0.25s, border-color 0.25s;
@@ -259,6 +288,8 @@ body {
 @media (max-width: 680px) {
   .header-nav { display: none; }
   .def-card { grid-template-columns: 1fr; }
+  .card-stack { --cols: 1 !important; }
+  .def-controls { gap: 1rem; }
   .hero h1 { font-size: 2.2rem; }
 }
 </style>
@@ -392,7 +423,24 @@ body {
   <p class="section-label">20 Core Concepts</p>
   <h2>What does it mean?</h2>
 
-  <div class="card-stack">
+  <div class="def-controls">
+    <div class="def-control">
+      <label for="defSort">Sort</label>
+      <select id="defSort" aria-label="Sort definitions">
+        <option value="curated">Curated</option>
+        <option value="az">Term A&rarr;Z</option>
+        <option value="za">Term Z&rarr;A</option>
+        <option value="category">Category</option>
+      </select>
+    </div>
+    <div class="def-control density">
+      <label for="defDensity">Density</label>
+      <input type="range" id="defDensity" min="1" max="3" step="1" value="1"
+             aria-label="Grid density (columns)">
+    </div>
+  </div>
+
+  <div class="card-stack" id="cardStack">
 
     <div class="def-card">
       <div class="def-term">Non-Woven</div>
@@ -604,6 +652,67 @@ toggle.addEventListener('click', () => {
   applyTheme(current === 'dark' ? 'light' : 'dark');
 });
 </script>
+<!-- Definition grid: sort + density controls (Steve standing rule) -->
+<script>
+(function(){
+  var stack = document.getElementById('cardStack');
+  var sortSel = document.getElementById('defSort');
+  var densInput = document.getElementById('defDensity');
+  if (!stack || !sortSel || !densInput) return;
+
+  // Snapshot original DOM order once (def-cards + narrative image-breaks).
+  var original = Array.prototype.slice.call(stack.children);
+  var cards  = original.filter(function(el){ return el.classList.contains('def-card'); });
+  var breaks = original.filter(function(el){ return el.classList.contains('image-break'); });
+
+  function termOf(el){ var t = el.querySelector('.def-term'); return t ? t.textContent.trim().toLowerCase() : ''; }
+  function catOf(el){ var t = el.querySelector('.def-tag'); return t ? t.textContent.trim().toLowerCase() : ''; }
+
+  function applySort(mode){
+    if (mode === 'curated') {
+      // Restore full original order, including the narrative image-breaks.
+      original.forEach(function(el){
+        if (el.classList.contains('image-break')) el.style.display = '';
+        stack.appendChild(el);
+      });
+      return;
+    }
+    // Reordered views: hide the narrative image-breaks (they only make sense in curated flow).
+    breaks.forEach(function(el){ el.style.display = 'none'; });
+    var sorted = cards.slice();
+    if (mode === 'az')       sorted.sort(function(a,b){ return termOf(a).localeCompare(termOf(b)); });
+    else if (mode === 'za')  sorted.sort(function(a,b){ return termOf(b).localeCompare(termOf(a)); });
+    else if (mode === 'category') sorted.sort(function(a,b){
+      var c = catOf(a).localeCompare(catOf(b));
+      return c !== 0 ? c : termOf(a).localeCompare(termOf(b));
+    });
+    sorted.forEach(function(el){ stack.appendChild(el); });
+  }
+
+  function applyDensity(n){
+    n = Math.max(1, Math.min(3, parseInt(n, 10) || 1));
+    stack.style.setProperty('--cols', n);
+    stack.classList.toggle('cols-multi', n > 1);
+  }
+
+  sortSel.addEventListener('change', function(){
+    applySort(sortSel.value);
+    try { localStorage.setItem('wpd-sort', sortSel.value); } catch(e){}
+  });
+  densInput.addEventListener('input', function(){
+    applyDensity(densInput.value);
+    try { localStorage.setItem('wpd-density', densInput.value); } catch(e){}
+  });
+
+  // Restore persisted choices (survive reload — standing rule).
+  var savedSort = null, savedDens = null;
+  try { savedSort = localStorage.getItem('wpd-sort'); savedDens = localStorage.getItem('wpd-density'); } catch(e){}
+  if (savedSort) sortSel.value = savedSort;
+  if (savedDens) densInput.value = savedDens;
+  applySort(sortSel.value);
+  applyDensity(densInput.value);
+})();
+</script>
 <!-- DW-HAMBURGER-NAV v1 (canonical 2026-05-10) -->
 <style id="dw-hamburger-styles">
   /* Canonical hamburger nav — always visible top-right, all viewports.

← 0fff334 add .deploy.conf for Kamatera deploy (port 9901, /health)  ·  back to Wallpaperdefinitions  ·  Remove Big Red widget — never UX-worked, collided with Help fc91905 →