fix: keep transitions consistent under browser-back and content fetching#152
Open
kimjh96 wants to merge 2 commits into
Open
fix: keep transitions consistent under browser-back and content fetching#152kimjh96 wants to merge 2 commits into
kimjh96 wants to merge 2 commits into
Conversation
Two root-cause fixes for transitions not running as defined on iOS Safari, both grounded in the same principle: everything the compiled CSS rule keys on must commit in one React paint, and the animation's start must not wait on the entering screen's content. 1. Shared bar rides a frame late on the browser back button. The bar's CSS ride rule keys on data-flemo-bar-status (rendered) AND data-flemo-bar-riding, which used to be written imperatively by a layout-effect (driveBarRiding). On a genuine popstate, React reconnects the unfrozen screen's effects as follow-up work, so the riding flag could land a frame after the status. Now computeBarRiding (pure, in @flemo/core) decides the ride and ScreenMotion renders data-flemo-bar-riding on the same element as the status, so they commit together for any trigger and any transition. 2. Transition pushed late / stutters when the entering screen fetches. The scope the keyframe animates and the consumer's children committed together, so a heavy cold mount (lazy chunk + fetch + large DOM) delayed the first paint, and thus the animation start, until that whole subtree committed. ScreenMotion now paints the scope first over an empty content box (the keyframe starts at once, over a cheap layer) and fills the children on the next, transition-priority commit. Gated to an entering push/replace screen hidden by its initial offset; root/SSR/pop/none render children directly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🦋 Changeset detectedLatest commit: e16ec6b The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Contributor
size-limit report 📦
|
commit: |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #152 +/- ##
==========================================
+ Coverage 84.05% 84.19% +0.14%
==========================================
Files 69 70 +1
Lines 1254 1240 -14
Branches 294 294
==========================================
- Hits 1054 1044 -10
Misses 119 119
+ Partials 81 77 -4
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
…n enter Refine the content-deferral gate. It keyed on "initial is non-empty", which also deferred transitions that stay visible on the first frame (e.g. the layout preset's opacity 0.97, or a scale), flashing a solid empty box. Extract the pure `initialHidesScreen` (fully transparent, or translated off-screen by >=100%) so deferral applies only when the empty box is invisible; partial fades/scales render content immediately as before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Two root-cause fixes (not band-aids) for transitions not running as defined on iOS Safari. Both follow one principle: everything the compiled CSS rule keys on must commit in one React paint, and the animation start must not wait on the entering screen's content.
What
1. Shared bar rides a frame late on the browser back button.
Screen A has a shared bar, B does not; from B you go back to A and A's bar must ride along. With a programmatic
pop()it was in sync; with the browser back button the bar lagged the screen by a frame. The bar's compiled ride rule keys ondata-flemo-bar-status(rendered) ANDdata-flemo-bar-riding, which was written imperatively by a layout effect (driveBarRiding). On a genuine popstate, React reconnects the unfrozen screen's effects as follow-up work, so the riding flag could present a frame after the status. NowcomputeBarRiding(a pure function in@flemo/core) decides the ride andScreenMotionrendersdata-flemo-bar-ridingon the same element as the status, so all four attributes commit together for any trigger and any transition.2. Transition pushed late / stutters when the entering screen fetches.
The scope the keyframe animates and the consumer's
{children}committed in one render, so a heavy cold mount (lazy chunk + fetch + large DOM) delayed the first paint, and thus the animation's start, until that whole subtree committed. First (cold) visit janked, second (warm) was smooth.ScreenMotionnow paints the scope first over an empty content box (the keyframe starts at once, over a cheap layer) and fills{children}on the next, transition-priority commit. Gated to an entering push/replace screen that itsinitialhides on the first frame; root, SSR, pop (Activity-preserved), andnonerender children directly.Why
Both are timing/compositor issues that survived the earlier
translateZcontent isolation, worst on a real iPhone GPU. Root-caused by tracing the React-commit / store-subscription ordering for each path, not by guessing.Impact
@flemo/coreminor: the internaldriveBarRidingengine helper is replaced by the purecomputeBarRiding. No documented consumer API changes.@flemo/reactpatch: both fixes are transparent to consumers (<Screen>{anything}</Screen>is unchanged).Test plan
pnpm turbo run typecheck lint test buildgreen (14/14).computeBarRidingunit tests;Screentests asserting the bar'sdata-flemo-bar-ridingco-renders with its status (rides when the partner lacks the bar, not when it owns it) and that anone-transition entering screen renders content immediately.Rollback
Revert the squash merge. No consumer code changes either way.