fix(desktop): outline the selected community (#4969)

**Category:** improvement
**User Impact:** Selected communities now use a clear offset outline
without tinting or covering their icon.

**Problem:** The selected community state replaced the icon surface with
an accent fill, obscuring image icons and changing the tile's content
treatment. Hover also changed the fill, text color, shape, and opacity,
making navigation states visually jumpy.

**Solution:** Preserve each community tile's neutral surface and content
while using a primary CSS outline for selection and a lighter outline
for hover. The transparent outline offset leaves the space around image
edges unpainted, and adjusted spacing prevents neighboring outlines from
colliding.

<img width="200" height="152" alt="Screen Recording 2026-08-05 at 3 23
32 PM"
src="https://github.com/user-attachments/assets/5c25b1c0-4be8-41c4-8f1d-ad0010310c92"
/>


<details>
<summary>File changes</summary>

**desktop/src/features/sidebar/ui/CommunityRail.tsx**
Replaces selected and hover fills with offset outlines, keeps icon
presentation stable across states, and adjusts rail and tooltip spacing
for the new outline geometry.

**desktop/tests/e2e/community-rail.spec.ts**
Covers the shared active/inactive surface, radius, text color, opacity,
and outline behavior, including hover invariants.

</details>

## Reproduction steps

1. Run the desktop app with two or more communities.
2. Give the active community an image icon.
3. Confirm the active icon keeps its original image and receives a 2px
primary outline with a transparent 2px gap.
4. Hover another community and confirm only a lighter outline appears;
its fill, text color, opacity, and corner radius remain unchanged.
5. Switch communities and confirm the outline follows the active
community.

## Screenshots

**Full desktop context**

![Selected community outline in the desktop
app](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/4969/selected-community-full.png)

---------

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
This commit is contained in:
Taylor Ho
2026-08-05 22:44:07 +00:00
committed by GitHub
parent 24c7995740
commit 005fe54d02
2 changed files with 56 additions and 9 deletions
@@ -106,7 +106,7 @@ function CommunityButton({
dragAttributes?: React.HTMLAttributes<HTMLElement>;
isDragging?: boolean;
}) {
const { mentionCount, showBadge, showDot, pending, badgeLabel } =
const { mentionCount, showBadge, showDot, badgeLabel } =
communityRailIndicators(unread);
const tooltipLabel = showBadge
@@ -135,11 +135,8 @@ function CommunityButton({
>
<span
className={cn(
"flex h-9 w-9 items-center justify-center overflow-hidden rounded-2xl text-xs font-semibold transition-all",
isActive
? "rounded-xl bg-primary text-primary-foreground"
: "bg-sidebar-accent/60 text-sidebar-foreground/80 hover:rounded-xl hover:bg-primary/80 hover:text-primary-foreground",
pending && !isActive && "opacity-60",
"flex h-9 w-9 items-center justify-center overflow-hidden rounded-xl bg-sidebar-accent/60 text-xs font-semibold text-sidebar-foreground/80 outline-2 outline-offset-2 outline-primary/0 transition-[outline-color]",
isActive ? "outline-primary" : "hover:outline-primary/50",
)}
>
{iconUrl ? (
@@ -172,7 +169,9 @@ function CommunityButton({
</button>
</ContextMenuTrigger>
</TooltipTrigger>
<TooltipContent side="right">{tooltipLabel}</TooltipContent>
<TooltipContent side="right" sideOffset={8}>
{tooltipLabel}
</TooltipContent>
</Tooltip>
<ContextMenuContent data-testid={`community-rail-menu-${community.id}`}>
{menu}
@@ -368,7 +367,7 @@ export function CommunityRail({
return (
<nav
aria-label="Communities"
className="relative z-0 flex w-14 shrink-0 flex-col items-center gap-2 overflow-y-auto bg-sidebar px-2.5 pb-5 pt-[calc(var(--buzz-top-chrome-height,40px)+7px)]"
className="relative z-0 flex w-14 shrink-0 flex-col items-center gap-2.5 overflow-y-auto bg-sidebar px-2.5 pb-5 pt-[calc(var(--buzz-top-chrome-height,40px)+7px)]"
data-testid="community-rail"
>
<DndContext
@@ -418,7 +417,9 @@ export function CommunityRail({
<Plus className="h-4 w-4" />
</button>
</TooltipTrigger>
<TooltipContent side="right">Add community</TooltipContent>
<TooltipContent side="right" sideOffset={8}>
Add community
</TooltipContent>
</Tooltip>
<EditCommunityDialog
onOpenChange={(open) => {
+46
View File
@@ -73,6 +73,52 @@ test.describe("community rail", () => {
"opacity",
"1",
);
await expect(buttonB.locator(":scope > span").first()).toHaveCSS(
"opacity",
"1",
);
const [activeStyle, inactiveStyle] = await Promise.all(
[buttonA, buttonB].map((button) =>
button
.locator(":scope > span")
.first()
.evaluate((element) => {
const style = getComputedStyle(element);
return {
backgroundColor: style.backgroundColor,
borderRadius: style.borderRadius,
color: style.color,
outlineStyle: style.outlineStyle,
outlineWidth: style.outlineWidth,
};
}),
),
);
expect(activeStyle.backgroundColor).toBe(inactiveStyle.backgroundColor);
expect(activeStyle.borderRadius).toBe(inactiveStyle.borderRadius);
expect(activeStyle.borderRadius).toBe("12px");
expect(activeStyle.color).toBe(inactiveStyle.color);
expect(activeStyle.outlineStyle).toBe("solid");
expect(activeStyle.outlineWidth).toBe("2px");
expect(inactiveStyle.outlineStyle).toBe("solid");
expect(inactiveStyle.outlineWidth).toBe("2px");
const inactiveIcon = buttonB.locator(":scope > span").first();
await buttonB.hover();
await expect(inactiveIcon).toHaveCSS("outline-width", "2px");
const hoverStyle = await inactiveIcon.evaluate((element) => {
const style = getComputedStyle(element);
return {
backgroundColor: style.backgroundColor,
borderRadius: style.borderRadius,
color: style.color,
outlineStyle: style.outlineStyle,
};
});
expect(hoverStyle.backgroundColor).toBe(inactiveStyle.backgroundColor);
expect(hoverStyle.borderRadius).toBe(inactiveStyle.borderRadius);
expect(hoverStyle.color).toBe(inactiveStyle.color);
expect(hoverStyle.outlineStyle).toBe("solid");
// The add-community affordance lives at the bottom of the rail.
await expect(page.getByTestId("community-rail-add")).toBeVisible();