From dfcdf47143bc6dc46643785bcc979ded98721e1c Mon Sep 17 00:00:00 2001 From: rays Date: Thu, 10 Sep 2026 18:39:25 +0000 Subject: [PATCH] Line up the feed sidebar Every row reserves the chevron slot, so artwork and titles share one column instead of stepping left when a feed has no children. Children keep a single icon size and read as nested from the indent alone. Labels stack on one line-height, and the unread count has a min-width so a three-digit feed doesn't shove its own title. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01AdXho5tTkjFLeUXKbEjKBh --- PROGRESS.md | 14 ++++++++++++++ tests/ui/app.spec.js | 3 ++- web/index.html | 19 +++++++++---------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/PROGRESS.md b/PROGRESS.md index 651729d..75418e7 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -56,6 +56,20 @@ and until now nothing set them. --- +## 2026-09-10 — Sidebar alignment + +The feed list had four different left edges: a row with no children skipped the chevron entirely, so +its artwork sat a chevron-width left of a folder's; children then used a different indent *and* a +smaller icon. Nothing lined up with anything. + +Every row now reserves the chevron slot whether or not it opens (an empty one is +`pointer-events:none`, so the click falls through to the row), all artwork is one size, and nesting +reads from the indent alone. Both label lines are `display:block` on a shared line-height instead of +an inline baseline, and the unread count has a `min-width` so three-digit feeds don't shove the +title. Rows came out shorter, so more feeds fit without scrolling. + +--- + ## 2026-09-10 — An item's picture An item's artwork now resolves in order of how deliberate the source is: `itunes:image`, then Media diff --git a/tests/ui/app.spec.js b/tests/ui/app.spec.js index b650f08..1a05c5c 100644 --- a/tests/ui/app.spec.js +++ b/tests/ui/app.spec.js @@ -174,7 +174,8 @@ test('an OPML subscription is a collapsible folder', async ({ page }) => { body: JSON.stringify({ feed: 'test-subscriptions', force: true }), })); - const chev = page.locator('.feed .chev'); + // Every row reserves the chevron slot for alignment; only a folder's is clickable. + const chev = page.locator('.feed.group .chev'); await expect(chev).toBeVisible({ timeout: 20_000 }); // Closed by default: the child is not listed until the folder is opened. diff --git a/web/index.html b/web/index.html index b909ad5..e852574 100644 --- a/web/index.html +++ b/web/index.html @@ -92,13 +92,13 @@ input:focus,select:focus{outline:0;border-color:var(--accent)} } .feed:hover{background:var(--panel2)} .feed.sel{background:var(--raise)} -.feed.child{margin-left:14px} -.feed.child .art{width:28px;height:28px;font-size:11px} +.feed.child{margin-left:16px} .chev{ flex:none;width:18px;height:18px;display:grid;place-items:center;border-radius:4px; color:var(--faint);font-size:11px;transition:transform .12s; } -.chev:hover{background:var(--raise);color:var(--fg)} +.chev:empty{pointer-events:none} +.chev:not(:empty):hover{background:var(--raise);color:var(--fg)} .chev.open{transform:rotate(90deg)} .childlist{display:grid;gap:4px;margin-top:10px} .childrow{ @@ -117,13 +117,13 @@ input:focus,select:focus{outline:0;border-color:var(--accent)} border-radius:7px;object-fit:cover;background:var(--raise);flex:none; display:grid;place-items:center;color:var(--faint);font-weight:700;overflow:hidden; } -.feed .art{width:38px;height:38px;font-size:14px} +.feed .art{width:34px;height:34px;font-size:13px} .feed .txt{min-width:0;flex:1} -.feed .txt b{display:block;font-weight:600;font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} -.feed .txt small{color:var(--faint);font-size:11.5px} +.feed .txt b{display:block;font-weight:600;font-size:13.5px;line-height:1.35;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +.feed .txt small{display:block;color:var(--faint);font-size:11.5px;line-height:1.35;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} .badge{ background:var(--accent);color:var(--ink);border-radius:20px;padding:1px 7px; - font-size:11px;font-weight:700;flex:none; + font-size:11px;font-weight:700;flex:none;min-width:26px;text-align:center; } .badge.zero{background:var(--raise);color:var(--faint)} @@ -461,7 +461,7 @@ function renderFeeds(){ el.className='feed'+(S.feed===f.id?' sel':'')+(depth?' child':'')+(kids?' group':''); const open = kids && (expanded.has(f.id) || q); el.innerHTML = - (kids?``:'')+ + `${kids?'▶':''}`+ artHTML(f.image,f.title||f.id)+ `
${esc(f.title||f.id)}`+ (kids?`${kids} feed${kids===1?'':'s'}${f.unread?` · ${f.unread} unread`:''}` @@ -470,8 +470,7 @@ function renderFeeds(){ (f.orphaned?'gone':'')+ `${f.unread}`; el.onclick=()=>{ selectFeed(f.id); $('#sidebar').classList.remove('open'); }; - const chev=$('.chev',el); - if(chev) chev.onclick=ev=>{ ev.stopPropagation(); toggleGroup(f.id); }; + if(kids) $('.chev',el).onclick=ev=>{ ev.stopPropagation(); toggleGroup(f.id); }; list.appendChild(el); } }