fix(nav): numeric inbox badge with active-state contrast

Show the unread count as a number on the collapsed nav rail (not just a dot), and
invert the badge colours on the active row so it isn't an accent-on-accent red blob;
centre the number on the circle.
This commit is contained in:
npeter83 2026-06-18 04:01:19 +02:00
parent ed4194a8d3
commit 00d07b9153

View file

@ -147,31 +147,47 @@ export default function NavSidebar({
"w-full flex items-center gap-3 rounded-lg px-2.5 py-2 text-sm transition"; "w-full flex items-center gap-3 rounded-lg px-2.5 py-2 text-sm transition";
const name = me.display_name ?? me.email.split("@")[0]; const name = me.display_name ?? me.email.split("@")[0];
const renderItem = ({ page: p, icon: Icon, label, badge }: NavItem) => ( const renderItem = ({ page: p, icon: Icon, label, badge }: NavItem) => {
<button const active = page === p;
key={p} // On the active row the background is the accent colour, so a same-accent badge would be
onClick={() => setPage(p)} // red-on-red. Invert it there (light pill, accent-coloured number) for clean contrast.
title={collapsed ? label : undefined} const badgeColor = active ? "bg-accent-fg text-accent" : "bg-accent text-accent-fg";
aria-current={page === p ? "page" : undefined} const show = !!badge && badge > 0;
className={`${rowBase} ${collapsed ? "justify-center px-0" : ""} relative ${ return (
page === p ? "bg-accent text-accent-fg" : "text-muted hover:text-fg hover:bg-card" <button
}`} key={p}
> onClick={() => setPage(p)}
<span className="relative shrink-0"> title={collapsed ? label : undefined}
<Icon className="w-[18px] h-[18px]" /> aria-current={active ? "page" : undefined}
{/* Collapsed rail: a small dot on the icon; expanded: a numeric pill at the row end. */} className={`${rowBase} ${collapsed ? "justify-center px-0" : ""} relative ${
{!!badge && badge > 0 && collapsed && ( active ? "bg-accent text-accent-fg" : "text-muted hover:text-fg hover:bg-card"
<span className="absolute -top-1 -right-1 w-2 h-2 rounded-full bg-accent ring-2 ring-bg" /> }`}
)} >
</span> <span className="relative shrink-0">
{!collapsed && <span className="truncate">{label}</span>} <Icon className="w-[18px] h-[18px]" />
{!!badge && badge > 0 && !collapsed && ( {/* Collapsed rail: a numeric badge centred on a circle at the icon corner; the ring
<span className="ml-auto min-w-[18px] h-[18px] px-1 rounded-full bg-accent text-accent-fg text-[11px] font-semibold inline-flex items-center justify-center tabular-nums"> matches the row background so it reads cleanly whether the row is active or not. */}
{badge > 99 ? "99+" : badge} {show && collapsed && (
<span
className={`absolute -top-2 -right-2 min-w-[16px] h-4 px-1 rounded-full text-[10px] font-bold leading-none grid place-items-center ring-2 ${badgeColor} ${
active ? "ring-accent" : "ring-bg"
}`}
>
{badge > 9 ? "9+" : badge}
</span>
)}
</span> </span>
)} {!collapsed && <span className="truncate">{label}</span>}
</button> {show && !collapsed && (
); <span
className={`ml-auto min-w-[18px] h-[18px] px-1.5 rounded-full text-[11px] font-semibold leading-none inline-flex items-center justify-center tabular-nums ${badgeColor}`}
>
{badge > 99 ? "99+" : badge}
</span>
)}
</button>
);
};
return ( return (
<nav <nav