Skip to content

Add conic gradients to View backgroundImage - #58143

Open
Stringsaeed wants to merge 10 commits into
react:mainfrom
Stringsaeed:feat/conic-gradient
Open

Stringsaeed wants to merge 10 commits into
react:mainfrom
Stringsaeed:feat/conic-gradient

Conversation

@Stringsaeed

@Stringsaeed Stringsaeed commented Aug 25, 2026 •

Copy link
Copy Markdown

Summary

Adds conic-gradient() support to View’s backgroundImage style on iOS and Android.

Supports:

  • String and object syntax.
  • Rotation with from <angle>.
  • Center positioning with at <position>, including edge offsets, percentages, fractional values, and negative offsets.
  • Angular and percentage color stops, two-position stops, and transition hints.
  • Multiple background layers and existing background sizing, positioning, and repeat behavior.

Implementation

  • Adds JavaScript and C++ parsing, a shared ConicGradient model, and native prop conversion.
  • Uses CAGradientLayer on iOS and SweepGradient on Android.
  • Renders the iOS sweep on a square layer to preserve angles on rectangular views. A clipped container retains the original tile dimensions.
  • Clears conflicting center defaults when explicit right or bottom offsets are supplied.
  • Accepts negative center offsets on Android.
  • Rejects malformed commas, misplaced hints, excessive stop positions, and invalid colors in conic gradient objects.

RNTester includes 16 corner cases with string/object syntax switching, dimension swapping, and hide/show controls.

Scope

This PR does not add:

  • repeating-conic-gradient().
  • CSS color-interpolation methods such as in hsl.
  • Explicit normalization for color stops outside the visible 0–360° interval.

Changelog:

[General] [Added] - Support conic gradients in View backgroundImage.

Test Plan

Automated checks

  • yarn flow-check: passed with 0 errors.

  • Focused ESLint and formatting checks: passed.

  • The following Fantom run passed all 732 tests:

    yarn fantom 'View-conicGradient-itest|processConicGradient-itest|processBackgroundImage-itest|processBackgroundPosition-itest|/View-itest|View-nativeCSSParsing-itest' --runInBand

    Coverage includes both native CSS parsing modes and both C++ prop-setter modes.

  • Android conic-gradient and color-stop tests passed all 18 tests:

    ./gradlew :packages:react-native:ReactAndroid:testDebugUnitTest
    --tests com.facebook.react.uimanager.style.ConicGradientTest
    --tests com.facebook.react.uimanager.style.ColorStopTest

    Three new negative-offset regressions failed before the fix and passed afterward.

  • All 11 iOS pixel tests passed through a focused XCTest runner linked against the production renderer and gradient utilities. Four test cases failed before the rectangular-rendering fix. This was not a run of the complete RNTester XCTest suite.

Runtime verification

Built and ran RNTester from this checkout on an iOS simulator and an Android emulator.

Checked all 16 corner cases, plus resizing, hiding, and restoring the gradient without remounting the view. Verified string and object syntax for positioning and invalid-input cases.

The Android negative-offset mismatch discovered during this verification was fixed and rechecked in RNTester. Both platforms now render the outside-center case as expected.

Screenshots are included below.

Outstanding validation

The public C++ API snapshots have not been regenerated. Snapshot generation and validation remain required before merge.

Visual verification

Tested in RNTester on iOS and Android. The “Conic Gradient Corner Cases” example includes 16 selectable cases, string/object syntax switching, resizing, and hide/show controls.

Geometry and rotation

The red wedge should retain its 45° angle across aspect ratios. The 90° and −270° rotations should look identical.

Case iOS Android
Wide rectangle 01-wide-string 01-wide-string
Tall rectangle 02-tall-string 02-tall-string
Extreme aspect ratio 03-extreme-aspect-string 03-extreme-aspect-string
90° rotation 04-rotation-string 04-rotation-string
Equivalent −270° rotation 10-negative-rotation-string 10-negative-rotation-string
Center positioning

The white ring marks the expected center where it falls within the view. Negative offsets place the center above and left of the view, producing a solid-blue visible area.

Case iOS Android
Right 10px / bottom 20px - string syntax 05-edge-offsets-string 05-edge-offsets-string
Right 10px / bottom 20px - object syntax 05-edge-offsets-object 05-edge-offsets-object
Percentage offsets - string syntax 06-percentage-offsets-string 06-percentage-offsets-string
Percentage offsets - object syntax 06-percentage-offsets-object 06-percentage-offsets-object
Zero edge offsets 07-zero-offsets-string 07-zero-offsets-string
Fractional offsets 08-fractional-offsets-string 08-fractional-offsets-string
Negative offsets - string syntax 09-negative-offsets-string 09-negative-offsets-string
Negative offsets - object syntax 09-negative-offsets-object 09-negative-offsets-object
Color stops, tiling, and composition

Repeated rectangular tiles should retain independent, undistorted centers. The transition hint controls the blend midpoint, and transparent portions of the conic layer reveal the underlying linear gradient.

Case iOS Android
Repeated rectangular tiles 11-repeated-tiles-string 11-repeated-tiles-string
Transition hint 12-transition-hint-string 12-transition-hint-string
Mixed background layers 13-mixed-layers-string 13-mixed-layers-string
Invalid input and zero-size views

Invalid gradients should leave only the gray fallback background. A zero-size view should paint nothing and must not crash.

Case iOS Android
Invalid trailing hint - string syntax 14-invalid-hint-string 14-invalid-hint-string
Invalid trailing hint - object syntax 14-invalid-hint-object 14-invalid-hint-object
Excessive stop positions - string syntax 15-invalid-stop-string 15-invalid-stop-string
Excessive stop positions - object syntax 15-invalid-stop-object 15-invalid-stop-object
Zero-size view 16-zero-size-string 16-zero-size-string
Updates without remounting

Swapping dimensions should recalculate the gradient geometry. Hiding should reveal the fallback background, and restoring should redraw the gradient correctly.

Case iOS Android
Resized in place 17-resized-in-place 17-resized-in-place
Gradient hidden 18-hidden 18-hidden
Gradient restored 19-restored 19-restored

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 25, 2026
@github-actions

Copy link
Copy Markdown

Warning

JavaScript API change detected

This PR commits an update to ReactNativeApi.d.ts, indicating a change to React Native's public JavaScript API.

  • Please include a clear changelog message.
  • This change will be subject to additional review.

This change was flagged as: POTENTIALLY_BREAKING

@Stringsaeed
Stringsaeed marked this pull request as ready for review August 29, 2026 21:56
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Aug 29, 2026
@Stringsaeed

Copy link
Copy Markdown
Author

Hey @intergalacticspacehighway, who help landing this PR?

Comment thread packages/react-native/React/Fabric/Utils/RCTConicGradient.mm Outdated
Comment on lines +24 to +28
if (gradient.position.top.has_value()) {
centerPoint.y = gradient.position.top->resolve(static_cast<float>(size.height));
} else if (gradient.position.bottom.has_value()) {
centerPoint.y = size.height - gradient.position.bottom->resolve(static_cast<float>(size.height));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we pass conic-gradient(from 0deg at right 10px bottom 20px, ...) the JS emits bottom 20, right 10 but BackgroundImagePropsConversions.cpp pre-sets position.top = 50% and position.left = 50% so bottom and right here would not override those values.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d3bc12e9f91. Explicit bottom now clears the default top, and explicit right clears the default left, while unspecified axes remain centered. Added regression coverage for string/object syntax across native CSS parsing and C++ prop-setter flag combinations, and verified the positioning in RNTester on both platforms.

@Stringsaeed

Copy link
Copy Markdown
Author

Do we need to separate the PR into smaller 2 PRs?

one for C++ foundation.
another one will be for complete cross-platform implementation.

@intergalacticspacehighway what do you think?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants