fix: GPU-promote the blog sticky header to kill the iOS WebKit nav flash (#610)#636
Merged
Merged
Conversation
#610 reproduces ONLY on iOS (both Safari and Chrome, both WebKit), never on desktop or Android. That is the signature of a WebKit position:sticky repaint bug: during a client-router forward nav the in-place content swap plus the instant scroll-to-top drives a scroll-time layer-position recompute that fails to clear the sticky header's repaint rect, so its background flashes for one frame (WebKit bugs 226532 / 276465 / 280316; corroborated by Apple dev forums 705172 and muffinman.io). The header is preserved across the swap, which is unique to webjs's partial-swap router (Turbo full-swaps, Next scrollIntoViews the new segment), so no other framework hits this. The proven fix is compositor-layer promotion ON the sticky element (never an ancestor, which breaks sticky in Safari): a static translateZ(0) plus backface-visibility:hidden and the -webkit- variants give the header a stable GPU layer that skips the bad repaint path. A static translateZ is cheaper than a permanent will-change. Ships with a `?nofix` A/B control: the header is preserved across a soft nav, so loading `/` (fix on) vs `/?nofix` (fix off) lets the flicker be confirmed and reproduced on-device in a single deploy. Re #610. Claude-Session: https://claude.ai/code/session_01W8RLiSnkwKXDmnkoasQfZF
This was referenced Jun 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause (now confirmed, not guessed)
#610 reproduces only on iOS (both Safari and Chrome on iOS, which are both WebKit), and is fine on desktop and Android. That is the signature of a WebKit
position: stickyrepaint bug, corroborated by deep research (web + the locally cloned frameworks):translateZ(0)/translate3d(0,0,0)) on the sticky element.<main>and scrolling to 0. Turbo full-swaps the body (header recreated); Next scrollIntoViews the new segment; Astro snapshots via View Transitions. None preserves a sticky header through a content-swap + scroll-to-0, so none triggers the bug.This is why the four prior attempts (backdrop-blur, opacity, scroll-behavior, the
data-navigatinggating) all failed: they targeted the header's paint inputs, not WebKit's repaint path. #622 attempted compositor promotion but I closed it untested while chasing the wrong theory.Fix
Promote the header to a stable GPU layer, on the sticky element itself (a transform on an ancestor breaks sticky in Safari):
A static
translateZ(cheaper than a permanentwill-change) gives the header its own layer so the scroll-time recompute can't leave a stale background.Built-in A/B control (one deploy confirms it)
The header is preserved across a soft nav, so the treatment set on the full page load persists through the forward navigation under test:
/= fix ON./?nofix= fix OFF (reproduces the flicker).Navigate forward (scroll the post list, tap a post) from each, on the iPhone. If
/is clean and/?nofixflashes, the fix and the diagnosis are both confirmed.Verification
/rendersclass="site-header ...",/?nofixrendersclass="site-header nofix ...",translateZ(0)present.webjs checkclean.Follow-up (after on-device confirmation)
Document the iOS sticky-header gotcha + the GPU-promotion pattern in
agent-docs/styling.md/lit-muscle-memory-gotchas.md, and remove the?nofixcontrol. Held until confirmed so we do not document an unverified fix.Re #610.
https://claude.ai/code/session_01W8RLiSnkwKXDmnkoasQfZF