[object Object]

← back to Crazy News Channel

cartoons gallery: add list/grid view toggle alongside density slider

6609a829074e8153e9ee3ad8dfb660c478930172 · 2026-09-24 10:24:06 -0700 · Steve Abrams

Adds a segmented Grid/List view-mode toggle to the P24 cartoon gallery
controls bar. List mode renders cards as full-width horizontal rows
(reusing the existing textContent-only card-building code via a
.list-mode CSS modifier) and hides the density slider since row width
is no longer density-controlled. View mode persists to localStorage
(p24cartoons.viewmode) alongside the existing sort/density keys.
Verified with Playwright at 1440px and 390px viewports: 0 console
errors, 0 page errors, correct persistence across reloads.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DqUe1Hvqsnrkxpe4R6jrk1

Files touched

Diff

commit 6609a829074e8153e9ee3ad8dfb660c478930172
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 24 10:24:06 2026 -0700

    cartoons gallery: add list/grid view toggle alongside density slider
    
    Adds a segmented Grid/List view-mode toggle to the P24 cartoon gallery
    controls bar. List mode renders cards as full-width horizontal rows
    (reusing the existing textContent-only card-building code via a
    .list-mode CSS modifier) and hides the density slider since row width
    is no longer density-controlled. View mode persists to localStorage
    (p24cartoons.viewmode) alongside the existing sort/density keys.
    Verified with Playwright at 1440px and 390px viewports: 0 console
    errors, 0 page errors, correct persistence across reloads.
    
    Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01DqUe1Hvqsnrkxpe4R6jrk1
---
 cartoons/index.html | 47 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 44 insertions(+), 3 deletions(-)

diff --git a/cartoons/index.html b/cartoons/index.html
index 1028d7c..8aa267f 100644
--- a/cartoons/index.html
+++ b/cartoons/index.html
@@ -33,15 +33,29 @@ main{max-width:1200px; margin:0 auto; padding:20px}
 .controls label{font-size:.8rem; color:var(--text-dim); display:flex; gap:8px; align-items:center}
 .controls select, .controls input[type=range]{accent-color:var(--accent)}
 .controls select{background:var(--chrome); color:var(--text); border:1px solid var(--panel-border); border-radius:6px; padding:4px 8px}
+.view-toggle{display:flex; gap:4px}
+.view-toggle button{background:var(--chrome); color:var(--text-dim); border:1px solid var(--panel-border); border-radius:6px; padding:5px 12px; font-size:.8rem; cursor:pointer; font-family:inherit}
+.view-toggle button:hover{color:var(--text)}
+.view-toggle button[aria-pressed="true"]{background:var(--accent); color:var(--chrome); font-weight:700; border-color:var(--accent)}
 #count{margin-left:auto; font-size:.8rem; color:var(--text-dim)}
 .grid{display:grid; grid-template-columns:repeat(auto-fill, minmax(var(--card-w), 1fr)); gap:18px}
 .card{background:var(--panel); border:1px solid var(--panel-border); border-radius:12px; overflow:hidden; display:flex; flex-direction:column; text-decoration:none; color:var(--text); transition:transform .15s ease, border-color .15s ease}
 .card:hover, .card:focus-visible{transform:translateY(-3px); border-color:var(--accent)}
 .card .thumb{aspect-ratio:4/3; background:var(--chrome) center/cover no-repeat; display:flex; align-items:center; justify-content:center; font-size:2.4rem}
-.card .body{padding:12px 14px; display:flex; flex-direction:column; gap:6px}
+.card .body{padding:12px 14px; display:flex; flex-direction:column; gap:6px; min-width:0}
 .card .title{font-weight:700; font-size:1rem}
 .card .meta{font-size:.75rem; color:var(--text-dim)}
 .card .blurb{font-size:.85rem; color:var(--text-dim)}
+.grid.list-mode{display:flex; flex-direction:column; gap:10px}
+.grid.list-mode .card{flex-direction:row; align-items:stretch}
+.grid.list-mode .card:hover, .grid.list-mode .card:focus-visible{transform:translateX(3px)}
+.grid.list-mode .card .thumb{aspect-ratio:auto; width:120px; min-width:120px; font-size:1.6rem}
+.grid.list-mode .card .body{flex:1; justify-content:center; min-width:0; overflow:hidden}
+.grid.list-mode .card .title, .grid.list-mode .card .blurb{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
+@media (max-width:480px){
+  .grid.list-mode .card .thumb{width:72px; min-width:72px; font-size:1.2rem}
+  .grid.list-mode .card .body{padding:10px}
+}
 .empty{background:var(--panel); border:1px dashed var(--panel-border); border-radius:12px; padding:40px 20px; text-align:center; color:var(--text-dim)}
 .empty a{display:inline-block; margin-top:10px}
 footer{max-width:1200px; margin:30px auto; padding:0 20px 30px; color:var(--text-dim); font-size:.75rem}
@@ -65,9 +79,13 @@ footer{max-width:1200px; margin:30px auto; padding:0 20px 30px; color:var(--text
         <option value="title">Title A&rarr;Z</option>
       </select>
     </label>
-    <label for="density">Density
+    <label for="density" id="densityLabel">Density
       <input type="range" id="density" min="220" max="420" step="20" value="300" aria-valuetext="card width">
     </label>
+    <div class="view-toggle" role="group" aria-label="View mode">
+      <button type="button" id="viewGridBtn" aria-pressed="true">Grid</button>
+      <button type="button" id="viewListBtn" aria-pressed="false">List</button>
+    </div>
     <span id="count"></span>
   </div>
 
@@ -85,8 +103,11 @@ footer{max-width:1200px; margin:30px auto; padding:0 20px 30px; color:var(--text
   const empty = document.getElementById('empty');
   const sortSel = document.getElementById('sortSel');
   const density = document.getElementById('density');
+  const densityLabel = document.getElementById('densityLabel');
+  const viewGridBtn = document.getElementById('viewGridBtn');
+  const viewListBtn = document.getElementById('viewListBtn');
   const count = document.getElementById('count');
-  const LS_SORT = 'p24cartoons.sort', LS_DENSITY = 'p24cartoons.density';
+  const LS_SORT = 'p24cartoons.sort', LS_DENSITY = 'p24cartoons.density', LS_VIEW = 'p24cartoons.viewmode';
 
   function safeGet(k, d){ try { return localStorage.getItem(k) ?? d; } catch { return d; } }
   function safeSet(k, v){ try { localStorage.setItem(k, v); } catch {} }
@@ -95,6 +116,26 @@ footer{max-width:1200px; margin:30px auto; padding:0 20px 30px; color:var(--text
   density.value = safeGet(LS_DENSITY, '300');
   document.documentElement.style.setProperty('--card-w', density.value + 'px');
 
+  let viewMode = safeGet(LS_VIEW, 'grid');
+  if (viewMode !== 'grid' && viewMode !== 'list') viewMode = 'grid';
+
+  function applyViewMode(){
+    grid.classList.toggle('list-mode', viewMode === 'list');
+    densityLabel.hidden = viewMode === 'list';
+    viewGridBtn.setAttribute('aria-pressed', String(viewMode === 'grid'));
+    viewListBtn.setAttribute('aria-pressed', String(viewMode === 'list'));
+  }
+  applyViewMode();
+
+  function setViewMode(mode){
+    if (mode !== 'grid' && mode !== 'list') return;
+    viewMode = mode;
+    safeSet(LS_VIEW, viewMode);
+    applyViewMode();
+  }
+  viewGridBtn.addEventListener('click', () => setViewMode('grid'));
+  viewListBtn.addEventListener('click', () => setViewMode('list'));
+
   function fmtDate(iso){
     try { return new Date(iso).toLocaleDateString(undefined, { year:'numeric', month:'short', day:'numeric' }); }
     catch { return iso; }

← 0368b35 crazy-news-channel: verify admin refresh endpoint live (sess  ·  back to Crazy News Channel  ·  admin-server: fix 3 verified security/robustness holes b0b4970 →