From 849df8bb3b95ade808b91b0230edd3143d2220ae Mon Sep 17 00:00:00 2001 From: Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> Date: Thu, 16 Jul 2026 14:49:34 +0800 Subject: [PATCH] fix(desktop): fade tab separators on hover, correct their weight (MUL-4811) (#5500) The tab strip's hairlines used --border, which is authored for near-white surfaces: its other uses sit on --surface-raised (L=1.0) where it gives a reasonable dL 0.055, but on --app-shell (L=0.964) it collapses to dL 0.019 -- about a quarter of the 0.068-0.080 the rest of the chrome's 1px keylines run at, so it read as almost invisible. Move both the tab separator and the pinned-zone divider to --surface-border, which the surrounding chrome (the active tab's border, the flare arcs, the content card's ring) already uses, and which is what the system draws for separators on --sidebar -- a surface whose lightness is identical to --app-shell. Also fade a hairline out while either tab it divides is hovered, so it no longer lingers 2px off the hover pill's rounded edge. The hairline sits on a tab's own left edge, so the pair's other half belongs to the neighbouring component instance and no ancestor-based variant can reach it; hence the adjacent-sibling custom variant. Co-authored-by: Claude Opus 4.8 Co-authored-by: multica-agent --- .../src/renderer/src/components/tab-bar.tsx | 12 +++++++++--- apps/desktop/src/renderer/src/globals.css | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/apps/desktop/src/renderer/src/components/tab-bar.tsx b/apps/desktop/src/renderer/src/components/tab-bar.tsx index a5d66211ad..15797258c0 100644 --- a/apps/desktop/src/renderer/src/components/tab-bar.tsx +++ b/apps/desktop/src/renderer/src/components/tab-bar.tsx @@ -185,7 +185,10 @@ function SortableTabItem({ canCloseOthers: boolean; isNew: boolean; shouldReduceMotion: boolean; - /** Hairline on the tab's left edge — hidden next to the active tab. */ + /** + * Hairline on the tab's left edge — hidden next to the active tab, and + * faded out while either of the two tabs it divides is hovered. + */ showSeparator: boolean; }) { const setActiveTab = useTabStore((s) => s.setActiveTab); @@ -356,9 +359,12 @@ function SortableTabItem({ /> )} {showSeparator && ( + // Fades in step with the neighbouring hover pill: both use a bare + // transition-opacity, so the hairline clears exactly as the pill + // arrives rather than lingering 2px off its rounded edge. )} @@ -650,7 +656,7 @@ export function TabBar() { unpinnedCount > 0 && (
)} diff --git a/apps/desktop/src/renderer/src/globals.css b/apps/desktop/src/renderer/src/globals.css index 6496a738d2..e67b990853 100644 --- a/apps/desktop/src/renderer/src/globals.css +++ b/apps/desktop/src/renderer/src/globals.css @@ -6,6 +6,20 @@ @custom-variant dark (&:is(.dark *)); +/* Tab strip hairlines sit on each tab's own left edge, so the hairline between + two tabs belongs to the tab on the right. Hovering the left tab has to fade + it out as well, which no ancestor-based variant can reach — hence the + adjacent-sibling selector. The (hover: hover) gate mirrors what Tailwind + already wraps group-hover in, so both halves of the fade stay in step on + devices without a pointer. */ +@custom-variant prev-tab-hover { + @media (hover: hover) { + [data-tab-frame]:hover + [data-tab-frame] & { + @slot; + } + } +} + /* Font stack: Inter for Latin UI text + system CJK fonts for localized content. Web app uses the same stack via next/font/google in apps/web/app/layout.tsx — keep the CJK fallback tail in sync across both files. The Inter primary family