Slide profile tabs away with the mobile top bar on scroll

The MobileTopBar slides off-screen on scroll-down, but ProfileTabs
stayed pinned at `top-mobile-bar`, leaving a translucent gap above
the tabs where the top bar used to be — and on scroll-up, the
returning top bar (z-20) visibly crossed over the top of the tab bar
(z-10) until it docked flush above them.

Mirror the global `SubHeaderBar`'s default behavior: track
`useNavHidden()` and apply `nav-hidden-slide` with a transform
transition so the tabs ride up off-screen together with the top bar.

Regression-of: 121991f3
This commit is contained in:
mkfain
2026-05-22 00:39:07 -05:00
parent 4fb67e3b1c
commit e1c66f3bba
+10
View File
@@ -1,5 +1,6 @@
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { cn } from '@/lib/utils';
import { useNavHidden } from '@/contexts/LayoutContext';
export interface ProfileTabsProps {
tabs: Array<{ id: string; label: string }>;
@@ -27,6 +28,7 @@ export function ProfileTabs({ tabs, activeTab, onChange }: ProfileTabsProps) {
const trackRef = useRef<HTMLDivElement>(null);
const tabRefs = useRef<Map<string, HTMLButtonElement>>(new Map());
const [indicator, setIndicator] = useState<{ left: number; width: number } | null>(null);
const navHidden = useNavHidden();
// Measure the active tab and update the underline indicator.
useLayoutEffect(() => {
@@ -72,6 +74,14 @@ export function ProfileTabs({ tabs, activeTab, onChange }: ProfileTabsProps) {
// matches the existing app convention so it sits flush with the
// mobile top nav. On desktop the chrome shifts and we use top-0.
'sticky top-mobile-bar sidebar:top-0 z-10',
// On mobile, slide out of view together with the MobileTopBar when
// the user scrolls down — otherwise the tabs sit at `top-mobile-bar`
// while the top bar slides away, leaving a translucent gap above
// them, and when the top bar slides back in it visibly crosses over
// the top of the tab bar (top bar is z-20, tabs z-10). Mirrors the
// global `SubHeaderBar`'s default behavior.
'max-sidebar:transition-transform max-sidebar:duration-300 max-sidebar:ease-in-out',
navHidden && 'nav-hidden-slide',
// Visual separation — translucent backdrop so feed content doesn't
// bleed through, with a single hairline border below.
'bg-background/80 backdrop-blur supports-[backdrop-filter]:bg-background/60',