From 0bb378e0ccfe96bb6e4298ad8faae4f0c710fa6c Mon Sep 17 00:00:00 2001 From: ctao/demo Date: Sat, 25 Jul 2026 23:56:05 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20typing-in-header=20scroll=20jump=20?= =?UTF-8?q?=E2=80=94=20global=20scroll-padding-top=20made=20Chrome's=20per?= =?UTF-8?q?-keystroke=20caret=20reveal=20walk=20the=20page=20to=200;=20off?= =?UTF-8?q?set=20moved=20to=20anchor=20targets=20(main=20[id]=20scroll-mar?= =?UTF-8?q?gin-top)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chrome re-scrolls the focused editable into view on every keystroke and honours the viewport's scroll-padding-top while ignoring the editable's own scroll-margin (crbug.com/41492445) — so the sticky-header search field, which sits above the 76px line and can never be scrolled below it, dragged the page toward 0 one keystroke at a time, and the previous negative-scroll-margin counter could not work. CDP harness bisection: of all suspects (smooth scroll, sticky header, overflow clip, animations, ambient layer, suggest siblings) only removing the global scroll-padding stops the jump. TOC/skip anchor offset now lives as scroll-margin-top on the in-content targets; harness confirms scrollY constant while typing and anchors still land below the header. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_012jWfn3RwPfFGTtBddm36Uy --- src/styles/global.css | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/styles/global.css b/src/styles/global.css index 4e67e4a..21845d2 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -107,14 +107,18 @@ } * { box-sizing: border-box; } -html { -webkit-text-size-adjust: 100%; scroll-padding-top: 76px; /* sticky header + anchor jumps */ } -/* Chrome re-scrolls the focused editable into view on EVERY keystroke and - honours scroll-padding-top while doing it. Inputs inside the sticky header - live above that 76px line by definition, so each typed character triggered - a corrective scroll-to-top. Negative scroll-margin cancels the padding for - exactly these fields (root cause, not a workaround — the padding exists for - in-content anchors, never for header chrome). */ -.nav-search input, .search-pop input { scroll-margin-top: -76px; } +html { -webkit-text-size-adjust: 100%; } +/* Anchor jumps (TOC, skip link) must land below the 76px sticky header. That + offset used to be a global `html { scroll-padding-top: 76px }` — but Chrome + re-scrolls the focused editable into view on EVERY keystroke and honours + scroll-padding-top while doing it (crbug.com/41492445: the reveal also + ignores the editable's own scroll-margin, so a negative scroll-margin on + the header inputs could not cancel it). The header search fields sit above + the 76px line by definition and, being sticky, can never be scrolled below + it — so each typed character walked the page toward 0. Scoping the offset + to the in-content anchor targets fixes typing and keeps every jump + (TOC headings, skip link → #main) landing below the header. */ +main, main [id] { scroll-margin-top: 76px; } /* Guard, not a solution (DESIGN.md): every wide element is contained at its own level (tables scroll in their box, long words wrap); clip only catches regressions so one offender can never remove the page gutters. `clip`, unlike