diff --git a/.changeset/fix_sidebar_badge_alignment.md b/.changeset/fix_sidebar_badge_alignment.md
new file mode 100644
index 000000000..65f35747f
--- /dev/null
+++ b/.changeset/fix_sidebar_badge_alignment.md
@@ -0,0 +1,5 @@
+---
+default: patch
+---
+
+Fix sidebar notification badge positioning so unread and unverified counts align consistently.
diff --git a/src/app/components/sidebar/SidebarCountBadge.test.tsx b/src/app/components/sidebar/SidebarCountBadge.test.tsx
new file mode 100644
index 000000000..dbcaa5cac
--- /dev/null
+++ b/src/app/components/sidebar/SidebarCountBadge.test.tsx
@@ -0,0 +1,36 @@
+import { render } from '@testing-library/react';
+import { describe, expect, it } from 'vitest';
+import { SidebarItem } from './SidebarItem';
+import { SidebarCountBadge } from './SidebarCountBadge';
+
+describe('SidebarCountBadge', () => {
+ it('uses the UnverifiedTab top-left numbered badge anchor', () => {
+ const { container, getByText } = render();
+
+ expect(container.firstElementChild).toHaveStyle({
+ top: '-0.375rem',
+ left: '-0.375rem',
+ right: 'auto',
+ });
+ expect(getByText('1')).toBeInTheDocument();
+ });
+
+ it('keeps the top-left anchor even when the parent contains a data-id button', () => {
+ const { container } = render(
+
+
+
+
+ );
+
+ const badge = container.querySelector('div[class*="Sidebar_SidebarItemBadge"]');
+ expect(badge).not.toBeNull();
+ expect(badge).toHaveStyle({
+ top: '-0.375rem',
+ left: '-0.375rem',
+ right: 'auto',
+ });
+ });
+});
diff --git a/src/app/components/sidebar/SidebarCountBadge.tsx b/src/app/components/sidebar/SidebarCountBadge.tsx
new file mode 100644
index 000000000..e6ce6de29
--- /dev/null
+++ b/src/app/components/sidebar/SidebarCountBadge.tsx
@@ -0,0 +1,20 @@
+import { Badge, Text, toRem } from 'folds';
+import { millify } from '$plugins/millify';
+import { SidebarItemBadge } from './SidebarItem';
+
+type SidebarCountBadgeProps = {
+ count: number;
+ variant: 'Warning' | 'Success' | 'Secondary';
+};
+
+export function SidebarCountBadge({ count, variant }: Readonly) {
+ return (
+
+
+
+ {millify(count)}
+
+
+
+ );
+}
diff --git a/src/app/components/sidebar/index.ts b/src/app/components/sidebar/index.ts
index 49e15b3e1..ba272a90f 100644
--- a/src/app/components/sidebar/index.ts
+++ b/src/app/components/sidebar/index.ts
@@ -1,4 +1,5 @@
export * from './Sidebar';
+export * from './SidebarCountBadge';
export * from './SidebarItem';
export * from './SidebarContent';
export * from './SidebarStack';
diff --git a/src/app/components/unread-badge/UnreadBadge.tsx b/src/app/components/unread-badge/UnreadBadge.tsx
index 1ad2605ab..cb7c0b2b5 100644
--- a/src/app/components/unread-badge/UnreadBadge.tsx
+++ b/src/app/components/unread-badge/UnreadBadge.tsx
@@ -4,7 +4,7 @@ import { millify } from '$plugins/millify';
import { useSetting } from '$state/hooks/settings';
import { settingsAtom } from '$state/settings';
-type UnreadBadgeProps = {
+export type UnreadBadgeProps = {
highlight?: boolean;
count: number;
/** Whether this badge belongs to a DM room. Used with the badgeCountDMsOnly setting. */
diff --git a/src/app/pages/client/sidebar/AccountSwitcherTab.tsx b/src/app/pages/client/sidebar/AccountSwitcherTab.tsx
index 75a7d099b..18ba4a568 100644
--- a/src/app/pages/client/sidebar/AccountSwitcherTab.tsx
+++ b/src/app/pages/client/sidebar/AccountSwitcherTab.tsx
@@ -27,10 +27,10 @@ import {
backgroundUnreadCountsAtom,
} from '$state/sessions';
import {
+ SidebarCountBadge,
SidebarItem,
SidebarItemTooltip,
SidebarAvatar,
- SidebarItemBadge,
} from '$components/sidebar';
import { UserAvatar } from '$components/user-avatar';
import { nameInitials } from '$utils/common';
@@ -287,12 +287,10 @@ export function AccountSwitcherTab() {
)}
{(totalBackgroundUnread > 0 || anyBackgroundHighlight) && (
-
-
-
+
)}
{unread && (unread.total > 0 || unread.highlight > 0) && (
- 0}
- style={{
- left: unread.total > 0 ? toRem(-6) : toRem(-4),
- right: 'auto',
- }}
- >
- 0}
- count={unread.highlight > 0 ? unread.highlight : unread.total}
- dm
- />
-
+ 0 ? 'Success' : 'Secondary'}
+ count={unread.highlight > 0 ? unread.highlight : unread.total}
+ />
)}
);
diff --git a/src/app/pages/client/sidebar/DirectTab.tsx b/src/app/pages/client/sidebar/DirectTab.tsx
index a1ff6ff58..362f0260f 100644
--- a/src/app/pages/client/sidebar/DirectTab.tsx
+++ b/src/app/pages/client/sidebar/DirectTab.tsx
@@ -12,12 +12,11 @@ import { getDirectPath, joinPathComponent } from '$pages/pathUtils';
import { useRoomsUnread } from '$state/hooks/unread';
import {
SidebarAvatar,
+ SidebarCountBadge,
SidebarItem,
- SidebarItemBadge,
SidebarItemTooltip,
} from '$components/sidebar';
import { useDirectSelected } from '$hooks/router/useDirectSelected';
-import { UnreadBadge } from '$components/unread-badge';
import { ScreenSize, useScreenSizeContext } from '$hooks/useScreenSize';
import { useNavToActivePathAtom } from '$state/hooks/navToActivePath';
import { markAsRead } from '$utils/notifications';
@@ -115,19 +114,10 @@ export function DirectTab() {
)}
{directUnread && (
- 0}
- style={{
- left: directUnread.total > 0 ? toRem(-6) : toRem(-4),
- right: 'auto',
- }}
- >
- 0}
- count={directUnread.highlight > 0 ? directUnread.highlight : directUnread.total}
- dm
- />
-
+ 0 ? 'Success' : 'Secondary'}
+ count={directUnread.highlight > 0 ? directUnread.highlight : directUnread.total}
+ />
)}
{menuAnchor && (
{homeUnread && (
- 0}
- style={{
- left: homeUnread.total > 0 ? toRem(-6) : toRem(-4),
- right: 'auto',
- }}
- >
- 0}
- count={homeUnread.highlight > 0 ? homeUnread.highlight : homeUnread.total}
- />
-
+ 0 ? 'Success' : 'Secondary'}
+ count={homeUnread.highlight > 0 ? homeUnread.highlight : homeUnread.total}
+ />
)}
{menuAnchor && (
)}
- {inviteCount > 0 && (
-
-
-
- )}
+ {inviteCount > 0 && }
);
}
diff --git a/src/app/pages/client/sidebar/SpaceTabs.tsx b/src/app/pages/client/sidebar/SpaceTabs.tsx
index c751f00cb..5cadc0c66 100644
--- a/src/app/pages/client/sidebar/SpaceTabs.tsx
+++ b/src/app/pages/client/sidebar/SpaceTabs.tsx
@@ -50,8 +50,8 @@ import { allRoomsAtom } from '$state/room-list/roomList';
import { getSpaceLobbyPath, getSpacePath, joinPathComponent } from '$pages/pathUtils';
import {
SidebarAvatar,
+ SidebarCountBadge,
SidebarItem,
- SidebarItemBadge,
SidebarItemTooltip,
SidebarStack,
SidebarStackSeparator,
@@ -60,7 +60,6 @@ import {
} from '$components/sidebar';
import { RoomUnreadProvider, RoomsUnreadProvider } from '$components/RoomUnreadProvider';
import { useSelectedSpace } from '$hooks/router/useSelectedSpace';
-import { UnreadBadge } from '$components/unread-badge';
import { getCanonicalAliasOrRoomId, isRoomAlias } from '$utils/matrix';
import { RoomAvatar } from '$components/room-avatar';
import { nameInitials, randomStr } from '$utils/common';
@@ -465,18 +464,10 @@ function SpaceTab({
)}
{unread && (
- 0}
- style={{
- left: unread.total > 0 ? toRem(-6) : toRem(-4),
- right: 'auto',
- }}
- >
- 0}
- count={unread.highlight > 0 ? unread.highlight : unread.total}
- />
-
+ 0 ? 'Success' : 'Secondary'}
+ count={unread.highlight > 0 ? unread.highlight : unread.total}
+ />
)}
{menuAnchor && (
{unread && (
- 0}
- style={{
- left: unread.total > 0 ? toRem(-6) : toRem(-4),
- right: 'auto',
- }}
- >
- 0}
- count={unread.highlight > 0 ? unread.highlight : unread.total}
- />
-
+ 0 ? 'Success' : 'Secondary'}
+ count={unread.highlight > 0 ? unread.highlight : unread.total}
+ />
)}
)}
diff --git a/src/app/pages/client/sidebar/UnverifiedTab.tsx b/src/app/pages/client/sidebar/UnverifiedTab.tsx
index a6c446b44..745fbdd8d 100644
--- a/src/app/pages/client/sidebar/UnverifiedTab.tsx
+++ b/src/app/pages/client/sidebar/UnverifiedTab.tsx
@@ -1,9 +1,9 @@
import { useState } from 'react';
-import { Badge, color, Icon, Icons, Text, toRem } from 'folds';
+import { color, Icon, Icons } from 'folds';
import {
SidebarAvatar,
+ SidebarCountBadge,
SidebarItem,
- SidebarItemBadge,
SidebarItemTooltip,
} from '$components/sidebar';
import { useDeviceIds, useDeviceList, useSplitCurrentDevice } from '$hooks/useDeviceList';
@@ -66,13 +66,7 @@ function UnverifiedIndicator() {
)}
{!unverified && unverifiedDeviceCount && unverifiedDeviceCount > 0 && (
-
-
-
- {unverifiedDeviceCount}
-
-
-
+
)}
)}