You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On a real iPhone, the interactive edge back-SWIPE gesture flashes a blank page for a few milliseconds before the previous page appears. The back BUTTON works fine. It is iOS-only: reproduces in both Safari and Chrome on iOS (both WebKit), and does NOT happen on Android or desktop.
This is distinct from the now-fixed #610 sticky-header flicker (that was the forward-nav header background; this is a whole-page blank on the back gesture).
Design / approach
Likely a router paint/snapshot-timing issue. During the interactive back-swipe, iOS renders a native snapshot of the previous page sliding in. But webjs is an SPA: it navigates via history.pushState, sets history.scrollRestoration = 'manual', and swaps the DOM on popstate. There is a window where the native snapshot is gone but the swapped-in DOM has not painted yet, so the page reads blank.
Candidate fixes to A/B on-device:
Defer the popstate swap to a requestAnimationFrame so WebKit paints at a frame boundary (the timing Turbo Drive uses for its render). An on-device flag window.__webjsDiag.raf was added then removed in fix: use position:fixed for the blog header to end the iOS nav flicker (#610) #640, but it was only evaluated against the FORWARD flash, not isolated against the back-swipe, so re-test it specifically here.
Restore from the in-memory snapshot synchronously / earlier so content is painted before the gesture settles.
Reconsider whether scrollRestoration = 'manual' interacts with the native gesture snapshot.
Implementation notes (for the implementing agent)
Where to look in packages/core/src/router-client.js: the popstate listener (onPopState, registered ~L184), history.scrollRestoration = 'manual' (~L203), and the popstate path in performNavigation (the isPopState cached-snapshot branch, snapshotGet then applySwap ~L855, then window.scrollTo).
Invariants: packages/ is plain .js + JSDoc, no .ts (AGENTS.md). The change must not regress the back/forward scroll-restoration UX (snapshot cache + manual scrollRestoration).
Tests + docs: a browser test should at least assert the popstate swap has painted content before the handler yields (the headline confirmation is manual on-device). If behavior changes, update agent-docs/advanced.md (client-router section).
Acceptance criteria
On a real iOS device (Safari + Chrome), the back-swipe gesture shows the previous page with no blank flash
The back BUTTON, forward nav, and back/forward scroll restoration are unchanged (no regression)
Android + desktop behavior unchanged
A browser test asserts the popstate swap paints content before yielding (counterfactual where feasible)
agent-docs/advanced.md updated if the client-router behavior changed
Problem
On a real iPhone, the interactive edge back-SWIPE gesture flashes a blank page for a few milliseconds before the previous page appears. The back BUTTON works fine. It is iOS-only: reproduces in both Safari and Chrome on iOS (both WebKit), and does NOT happen on Android or desktop.
This is distinct from the now-fixed #610 sticky-header flicker (that was the forward-nav header background; this is a whole-page blank on the back gesture).
Design / approach
Likely a router paint/snapshot-timing issue. During the interactive back-swipe, iOS renders a native snapshot of the previous page sliding in. But webjs is an SPA: it navigates via
history.pushState, setshistory.scrollRestoration = 'manual', and swaps the DOM onpopstate. There is a window where the native snapshot is gone but the swapped-in DOM has not painted yet, so the page reads blank.Candidate fixes to A/B on-device:
requestAnimationFrameso WebKit paints at a frame boundary (the timing Turbo Drive uses for its render). An on-device flagwindow.__webjsDiag.rafwas added then removed in fix: use position:fixed for the blog header to end the iOS nav flicker (#610) #640, but it was only evaluated against the FORWARD flash, not isolated against the back-swipe, so re-test it specifically here.scrollRestoration = 'manual'interacts with the native gesture snapshot.Implementation notes (for the implementing agent)
packages/core/src/router-client.js: thepopstatelistener (onPopState, registered ~L184),history.scrollRestoration = 'manual'(~L203), and the popstate path inperformNavigation(theisPopStatecached-snapshot branch,snapshotGetthenapplySwap~L855, thenwindow.scrollTo).raflever was already written and reverted (see chore: on-device isolation flags for the #610 iOS nav repaint (diagnostic) #637/fix: use position:fixed for the blog header to end the iOS nav flicker (#610) #640 history) so the diff is small to re-introduce.packages/is plain.js+ JSDoc, no.ts(AGENTS.md). The change must not regress the back/forward scroll-restoration UX (snapshot cache + manual scrollRestoration).agent-docs/advanced.md(client-router section).Acceptance criteria
agent-docs/advanced.mdupdated if the client-router behavior changed