Skip to content

feat: Run opacity CSS transitions on the Android platform path - #10061

Closed
MatiPl01 wants to merge 5 commits into
mainfrom
@matipl01/android-css-opacity-transitions
Closed

MatiPl01 wants to merge 5 commits into
mainfrom
@matipl01/android-css-opacity-transitions

Conversation

@MatiPl01

@MatiPl01 MatiPl01 commented Jul 27, 2026 •

Copy link
Copy Markdown
Contributor

Routes opacity to the Android backend behind ANDROID_CSS_PLATFORM_TRANSITIONS, off by default.

Every CSS easing routes: a TimeInterpolator can carry any curve, and Kotlin rebuilds steps() and linear() stops from the same normalized points the loop uses, so nothing reimplements the easing spec across the boundary. Apple stays on linear and cubic-bezier, which is all CAMediaTimingFunction can express.

Stacked on #10060.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 03f3f73f-1c94-412f-85bc-e5da307e6c5c

📥 Commits

Reviewing files that changed from the base of the PR and between 50b1385 and 824b5c8.

📒 Files selected for processing (1)
  • packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/css/CSSPlatformTransitionsManager.kt

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added native Android CSS transitions for opacity animations.
    • Enabled the transition feature in the Android, macOS, and tvOS example configurations.
  • Bug Fixes

    • Improved Android transition timing for delays, easing, and animations that have already started.
  • Documentation

    • Clarified platform-specific CSS property and easing support for native transitions.

Walkthrough

This change adds the ANDROID_CSS_PLATFORM_TRANSITIONS flag, enables it in example applications, routes Android opacity transitions through native handling, and updates Android animator timing for delayed and elapsed transitions.

Changes

Android CSS platform transitions

Layer / File(s) Summary
Feature flag configuration
packages/react-native-reanimated/src/featureFlags/*, apps/*/package.json
Defines ANDROID_CSS_PLATFORM_TRANSITIONS as disabled by default and enables it in the Fabric, macOS, and tvOS example configurations.
CSS property routing
packages/react-native-reanimated/Common/cpp/reanimated/CSS/utils/platform.*
When the flag is active on Android, native routing accepts all easing types but routes only the opacity property. Unsupported properties continue through the C++ loop.
Transition timing and startup
packages/react-native-reanimated/android/src/main/java/com/swmansion/reanimated/css/CSSPlatformTransitionsManager.kt
Uses HoldThenEase for explicit delay handling, includes delay in the animator duration, seeks elapsed transitions, and registers the animator after timeline setup.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CSSPlatformTransitionsManager
  participant ObjectAnimator
  participant HoldThenEase
  CSSPlatformTransitionsManager->>ObjectAnimator: configure delay-inclusive duration
  CSSPlatformTransitionsManager->>HoldThenEase: wrap configured easing
  ObjectAnimator->>HoldThenEase: interpolate elapsed transition
  CSSPlatformTransitionsManager->>ObjectAnimator: seek and start animator
Loading

Possibly related PRs

Suggested reviewers: pawicao, tjzel

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: enabling opacity CSS transitions on the Android platform path.
Description check ✅ Passed The description accurately explains the Android feature flag, opacity routing, easing support, and platform behavior.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch @matipl01/android-css-opacity-transitions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@MatiPl01 MatiPl01 self-assigned this Jul 27, 2026
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from e028a18 to b66f94d Compare July 27, 2026 07:49
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from c4bb6d7 to 4d865ae Compare July 27, 2026 10:15
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from b66f94d to 6a57755 Compare July 27, 2026 10:17
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from 4d865ae to fc321a9 Compare July 27, 2026 11:34
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 6a57755 to 6c2f1b1 Compare July 27, 2026 11:35
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from fc321a9 to a1a4c84 Compare July 27, 2026 11:48
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 6c2f1b1 to 3821b05 Compare July 27, 2026 11:49
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from 5045268 to 7eee6dd Compare July 29, 2026 10:29
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 3821b05 to ab00630 Compare July 29, 2026 10:31
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from 7eee6dd to 94063c7 Compare July 29, 2026 12:23
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from ab00630 to 389daec Compare July 29, 2026 12:25
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from 94063c7 to 1357669 Compare July 29, 2026 18:39
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 389daec to 86093b0 Compare July 29, 2026 18:43
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from 1357669 to 1e0895d Compare July 29, 2026 22:41
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 86093b0 to 162ed5c Compare July 29, 2026 22:41
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from 1e0895d to db219a6 Compare July 29, 2026 23:01
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 162ed5c to 7ff067b Compare July 29, 2026 23:02
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from db219a6 to 0eef8f2 Compare July 30, 2026 06:46
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 7ff067b to 236735e Compare July 30, 2026 06:46
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from 0eef8f2 to 8681967 Compare July 30, 2026 13:21
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 236735e to 0e03087 Compare July 30, 2026 13:21
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from 8681967 to 2cd0923 Compare July 30, 2026 13:50
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 0e03087 to 270d689 Compare July 30, 2026 13:50
@MatiPl01
MatiPl01 marked this pull request as ready for review July 30, 2026 20:35
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 270d689 to ebe9bdf Compare July 30, 2026 23:16
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from b475585 to 942016e Compare August 11, 2026 16:39
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from c509641 to e1cfb18 Compare August 11, 2026 16:39
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 76b41d5 to 9116dc6 Compare August 11, 2026 23:56
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from 43c2fd7 to 0ec3bc1 Compare August 12, 2026 07:50
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 9116dc6 to 50b1385 Compare August 12, 2026 07:50
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from 0ec3bc1 to 6721cdb Compare August 12, 2026 08:03
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 50b1385 to dbdc501 Compare August 12, 2026 08:03
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from 6721cdb to a06bc94 Compare August 12, 2026 08:11
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from dbdc501 to 90753d5 Compare August 12, 2026 08:11
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from a06bc94 to d87aa6d Compare August 12, 2026 08:56
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 90753d5 to 824b5c8 Compare August 12, 2026 08:56
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from d87aa6d to 010000d Compare August 12, 2026 10:58
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 824b5c8 to 66fddc4 Compare August 12, 2026 10:58
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-platform-transition-backend branch from 010000d to 8fe070b Compare August 12, 2026 11:31
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 24a0080 to 2c79ccd Compare August 12, 2026 11:31
@MatiPl01
MatiPl01 requested a review from pawicao August 12, 2026 11:38
Base automatically changed from @matipl01/android-css-platform-transition-backend to main August 12, 2026 12:33
Routes opacity to the Android backend behind ANDROID_CSS_PLATFORM_TRANSITIONS,
off by default. The backend declines when the view predates API 29 or the
target is 0, and those transitions fall back to the C++ loop.
@MatiPl01
MatiPl01 force-pushed the @matipl01/android-css-opacity-transitions branch from 0db10d0 to f930a96 Compare August 12, 2026 12:33
@MatiPl01

Copy link
Copy Markdown
Contributor Author

Split into focused PRs, since this bundled routing, delay playback and teardown under a title describing only the first:

@MatiPl01 MatiPl01 closed this Aug 12, 2026
MatiPl01 added a commit that referenced this pull request Aug 13, 2026
The pre-draw repair re-asserts every running platform transition on
every frame, but React can only overwrite an animated value on frames
where it wrote props. A `UIManagerListener` records mounts and the
repair returns immediately on all other frames, so the walk over running
transitions happens at write rate rather than at the display refresh
rate.

That matters most after a transition settles: a persistent hold keeps
its entry, so without the gate every later frame the window draws keeps
walking the map. Measured on an emulator during a 3s transition, 201 of
240 pre-draw callbacks returned early.

Synchronous updates run their mount item inline
(`FabricUIManager.synchronouslyUpdateViewOnUIThread`), bypassing
`MountItemDispatcher`, so no listener fires for them even though
`opacity` is one of the props they carry. They therefore open the gate
explicitly, which makes the invariant true by construction rather than
by coincidence. Measured on device, such writes are rare and always
landed on a frame where a mount had already opened the gate, so this is
insurance rather than an observed fix.

Identical in content to #10168, which this replaces. That one was based
on the head branch of #10061, and closing #10061 instead of merging it
left the base pointing at a branch with no live PR, so merging would not
have reached `main`. GitHub refuses to change the base of a pull request
that belongs to a stack, so recreating it was the only way to target
`main`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant