From 573ba52f6346b1bc072880fe5c4ed71d3dc1ca95 Mon Sep 17 00:00:00 2001 From: Ulas Kalayci Date: Sun, 19 Apr 2026 17:26:12 +0200 Subject: [PATCH] fix: anchor overdue badge to icon via runtime wrapper (#56) Root cause: the badge was `position: absolute` relative to the entire `.nav-item`, which stretches to `flex: 1` on mobile (up to ~75 px wide). With `right: 4px` the badge sat far from the icon on the bottom bar and overlapped label text in the expanded desktop sidebar. Fix: `updateOverdueBadge()` now wraps the nav icon in a `.nav-item__icon-wrap` span (created once, reused on subsequent calls). The badge is appended there instead of to the nav item root. CSS changes: - Remove `.nav-item .nav-badge` positional override - Add `.nav-item__icon-wrap { position: relative; display: inline-flex }` - Add `.nav-item__icon-wrap .nav-badge { position: absolute; top: -4px; right: -4px }` The badge now consistently overlays the top-right corner of the icon across all nav layouts (mobile column, collapsed sidebar row, expanded sidebar row with label). Co-Authored-By: Claude Sonnet 4.6 --- public/pages/tasks.js | 16 ++++++++++++++-- public/styles/layout.css | 12 +++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) mode change 100644 => 100755 public/pages/tasks.js mode change 100644 => 100755 public/styles/layout.css diff --git a/public/pages/tasks.js b/public/pages/tasks.js old mode 100644 new mode 100755 index fa1ccfb..a505041 --- a/public/pages/tasks.js +++ b/public/pages/tasks.js @@ -827,11 +827,23 @@ function updateOverdueBadge() { document.querySelectorAll('[data-route="/tasks"] .nav-badge').forEach((el) => el.remove()); if (overdue > 0) { - document.querySelectorAll('[data-route="/tasks"]').forEach((el) => { + document.querySelectorAll('[data-route="/tasks"]').forEach((navItem) => { + let anchor = navItem.querySelector('.nav-item__icon-wrap'); + if (!anchor) { + const icon = navItem.querySelector('.nav-item__icon'); + anchor = document.createElement('span'); + anchor.className = 'nav-item__icon-wrap'; + if (icon) { + icon.replaceWith(anchor); + anchor.appendChild(icon); + } else { + navItem.prepend(anchor); + } + } const badge = document.createElement('span'); badge.className = 'nav-badge'; badge.textContent = String(overdue); - el.appendChild(badge); + anchor.appendChild(badge); }); } } diff --git a/public/styles/layout.css b/public/styles/layout.css old mode 100644 new mode 100755 index 23343e2..6b83ed2 --- a/public/styles/layout.css +++ b/public/styles/layout.css @@ -196,10 +196,16 @@ position: relative; } -.nav-item .nav-badge { +.nav-item__icon-wrap { + position: relative; + display: inline-flex; + flex-shrink: 0; +} + +.nav-item__icon-wrap .nav-badge { position: absolute; - top: var(--space-1); - right: var(--space-1); + top: -4px; + right: -4px; margin-left: 0; }