From 5c9e9254d163dfa3ce6e17ee05d3e5dc1aecd9c5 Mon Sep 17 00:00:00 2001 From: th Date: Thu, 16 Apr 2026 17:35:27 +0200 Subject: [PATCH] fix(shopping): remove old flat-list li styles causing visual doubling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .shopping-items li was the old single-list item rule. After the multi-list refactor, the direct children of .shopping-items are
  • wrappers, not individual items. The old rule gave the box the same background + border + padding + tap-target as the .shopping-item divs inside it — making each list look like it appeared twice. Drop the generic li rule entirely. Items are styled via .shopping-item, boxes via .shopping-list-box, empty state via .shopping-items .empty. Also dedup by card_uid in loadShopping JS as a safety net. Co-Authored-By: Claude Opus 4.6 (1M context) --- relay/ui/app.js | 9 ++++++++- relay/ui/style.css | 12 ++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/relay/ui/app.js b/relay/ui/app.js index e8903b9..4be75a5 100644 --- a/relay/ui/app.js +++ b/relay/ui/app.js @@ -773,7 +773,14 @@ async function loadShopping(quiet = false) { const list = $('shopping-items'); if (!list) return; if (!data?.ok) { if (!quiet) shoppingErr(data?.error || 'load failed'); return; } - const lists = data.lists || []; + // Dedup by card_uid — belt and suspenders in case the relay DB + // has stale rows from earlier schema migrations. + const seen = new Set(); + const lists = (data.lists || []).filter(l => { + if (seen.has(l.card_uid)) return false; + seen.add(l.card_uid); + return true; + }); if (!lists.length) { if (title) title.textContent = 'Shopping'; list.innerHTML = '
  • no active shopping lists
  • '; diff --git a/relay/ui/style.css b/relay/ui/style.css index 28cc96a..b44f8e9 100644 --- a/relay/ui/style.css +++ b/relay/ui/style.css @@ -267,16 +267,8 @@ label.checkbox input[type="checkbox"] { accent-color:var(--accent); cursor:point unchecked items only, tap to flip. */ .shopping-wrap { max-width: 520px; margin: 0 auto; display: flex; flex-direction: column; gap: 12px; } .shopping-top { display: flex; align-items: center; gap: 10px; padding: 6px 2px; } -.shopping-items { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 6px; } -.shopping-items li { - background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); - padding: 14px 16px; font-size: 16px; cursor: pointer; user-select: none; - -webkit-tap-highlight-color: transparent; - transition: transform 0.12s, opacity 0.3s, background 0.12s; -} -.shopping-items li:active { transform: scale(0.98); background: var(--raised, var(--surface)); } -.shopping-items li.checked { opacity: 0.35; text-decoration: line-through; } -.shopping-items .empty { text-align: center; padding: 40px 16px; color: var(--dim); background: transparent; border: none; cursor: default; list-style: none; } +.shopping-items { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 10px; } +.shopping-items .empty { list-style: none; text-align: center; padding: 40px 16px; color: var(--dim); background: transparent; border: none; cursor: default; } .shopping-items .empty:active { transform: none; } /* Multi-list stacked boxes */