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 <noreply@anthropic.com>
This commit is contained in:
Ulas Kalayci
2026-04-19 17:26:12 +02:00
parent 592287ea4e
commit 573ba52f63
2 changed files with 23 additions and 5 deletions
Regular → Executable
+14 -2
View File
@@ -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);
});
}
}