From 7a938b7e9ef06d830a833a44c57c5fa9650bec52 Mon Sep 17 00:00:00 2001 From: AmintaCCCP Date: Sat, 29 Aug 2026 13:42:37 +0800 Subject: [PATCH] fix(ui): keep header above search bar; lock root scroller under modal sheets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Header sticky top bar raised z-40 -> z-50. The repository search bar's input row sits at relative z-40 later in the DOM, so once it scrolled up to the header band it painted on top of the header (same stacking level, later order wins). The header is page chrome and must sit above in-flow page content; Radix portals keep stacking above it via document order. - Modal scroll lock now reaches the root scroller. react-remove-scroll only writes overflow on body, but body overflow never propagates to the viewport because html owns overflow (overflow-y: scroll), so the page behind the RepositoryChatSheet overlay stayed scrollable (scrollbar drag + wheel) — the "second scrollbar" next to the sheet's message list. html:has(body[data-scroll-locked]) { overflow: hidden } locks the real scroller; hidden (not clip) keeps the viewport a scroll container so the scroll offset, the sticky header anchor and the scrollbar-gutter reservation all survive the lock, and the lock releases on close. Verified with a Playwright E2E against the dev server (mocked GitHub API): 12/12 checks — header paints above the search bar at overlap, sheet open freezes page scroll (wheel + offset preserved), unlock restores scrolling. --- src/components/Header.tsx | 2 +- src/index.css | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 0cdad429..41946436 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -56,7 +56,7 @@ export const Header: React.FC = () => { const t = (zh: string, en: string) => language === 'zh' ? zh : en; return ( -
+
{/* Logo and Title */} diff --git a/src/index.css b/src/index.css index d85fab12..3595eade 100644 --- a/src/index.css +++ b/src/index.css @@ -455,6 +455,15 @@ html body[data-scroll-locked] { overflow: clip !important; } +/* body's overflow never propagates to the viewport (html owns overflow), so the + root scroller must be locked directly or the page behind a modal overlay keeps + scrolling (scrollbar drag + wheel). hidden — not clip — keeps the viewport a + scroll container, so the scroll offset, the sticky header's anchor and the + scrollbar-gutter: stable reservation all survive the lock. */ +html:has(body[data-scroll-locked]) { + overflow: hidden !important; +} + /* 窗口滚动条颜色由 --ui-line-strong 等 token 统一驱动(见文件底部 Linear 段落)。 */