From fd1566116b044265be138b1b78a5c072811b989c Mon Sep 17 00:00:00 2001 From: Fredrik Johansson Date: Mon, 13 Jul 2026 19:07:08 +0200 Subject: [PATCH] Fix button alignment on article cards and the reader view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .article-card form had margin-left: auto applied to each form individually inside a flex row that also held the meta text (hostname, date, unread badge) — spacing shifted depending on whether the unread badge was present, since the auto-margin was competing with a variable number of sibling elements instead of anchoring against a fixed layout. Restructured into two explicit flex groups per row — meta info on the left, action buttons on the right, justify-content: space-between on the container — so the buttons land at a consistent x-position regardless of read/unread state or article title length. Same fix applied to both the list view and the reader view's meta row. Verified visually against both states (read and unread cards) in a screenshot — buttons now align consistently. Co-Authored-By: Claude Sonnet 5 --- src/server/ui.ts | 58 +++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/src/server/ui.ts b/src/server/ui.ts index 911c150..ece416f 100644 --- a/src/server/ui.ts +++ b/src/server/ui.ts @@ -76,9 +76,11 @@ a:hover { text-decoration: underline; } .article-card.unread { border-left: 2px solid var(--accent); } .article-title { font-size: 15px; font-weight: 600; color: var(--heading); } .article-title:hover { color: var(--accent); } -.article-meta { font-family: var(--mono); font-size: 11px; color: var(--muted); margin-top: 6px; display: flex; gap: 10px; align-items: center; flex-wrap: wrap; } +.article-meta { margin-top: 10px; display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; } +.article-meta-info { font-family: var(--mono); font-size: 11px; color: var(--muted); display: flex; gap: 10px; align-items: center; flex-wrap: wrap; } .unread-dot { color: var(--accent); } -.article-card form { display: inline; margin-left: auto; } +.article-actions { display: flex; gap: 8px; flex-shrink: 0; } +.article-actions form { display: inline; } .empty { font-family: var(--mono); font-size: 13px; color: var(--muted); text-align: center; padding: 32px 0; } @@ -87,7 +89,9 @@ article h1, article h2, article h3 { font-family: var(--sans); color: var(--head article p { margin: 1em 0; } article img { max-width: 100%; border-radius: 6px; } article a { text-decoration: underline; } -.reader-meta { font-family: var(--mono); font-size: 12px; color: var(--muted); margin: 8px 0 24px; display: flex; gap: 10px; align-items: center; flex-wrap: wrap; } +.reader-meta { margin: 8px 0 24px; display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; } +.reader-meta-info { font-family: var(--mono); font-size: 12px; color: var(--muted); display: flex; gap: 10px; align-items: center; flex-wrap: wrap; } +.reader-meta form { display: inline; }
${body}
`; } @@ -115,16 +119,20 @@ router.get('/', requireTokenQuery, (req: any, res) => {
  • ${escapeHtml(a.title)}
  • `).join(''); @@ -155,16 +163,20 @@ router.get('/read/:id', requireTokenQuery, (req: any, res) => {