Skip to content

Honor right and bottom backgroundPosition offsets on iOS - #59990

Open
jpudysz wants to merge 1 commit into
react:mainfrom
jpudysz:fix/ios-background-position-right-bottom
Open

jpudysz wants to merge 1 commit into
react:mainfrom
jpudysz:fix/ios-background-position-right-bottom

Conversation

@jpudysz

@jpudysz jpudysz commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

Summary:

On iOS, a backgroundPosition measured from the right or bottom edge is ignored. The image is drawn at the top left. Android draws it correctly.

<View
  style={{
    width: 200,
    height: 100,
    backgroundImage: 'radial-gradient(circle, #ff6b6b, #4ecdc4)',
    backgroundRepeat: 'no-repeat',
    backgroundSize: '50px 50px',
    backgroundPosition: 'right 8px bottom 8px', // or [{bottom: 8, right: 8}]
  }}
/>

Root cause

  • BackgroundPosition() sets top and left to 0 by default.
  • processBackgroundPosition sends one edge per axis, for example {bottom: 8, right: 8}. fromRawValue in conversions.h fills bottom and right but keeps the default top and left, so all four edges end up set.
  • RCTBackgroundImageUtils uses top and left when they are set, so bottom and right never apply.

Android's Props 2.0 path already works around this. toDynamic in HostPlatformViewProps.cpp prefers bottom and right, with the comment "JS sends one edge per axis, but the parser keeps the default top and left next to a bottom or right" (#59078). The bug has been there since #52283.

Fix

The parser now drops the default top when the value sets bottom without a top, and the default left when it sets right without a left. A top or left given explicitly is kept and still wins, which matches Android's BackgroundPosition.parse. The default constructor is unchanged, so a View with no backgroundPosition still positions at 0 0.

File Change
ReactCommon/react/renderer/components/view/conversions.h Drop the default top / left when bottom / right is set without them
ReactCommon/react/renderer/components/view/tests/ConversionsTest.cpp Two parser tests

Behavior across platforms

Path Before After
iOS right / bottom ignored, image at top left image placed from the right / bottom edge
Android (default, Java props) correct, does not use this parser unchanged
Android Props 2.0 (toDynamic) correct via workaround unchanged, same payload
Explicit top / left next to bottom / right (object form) top / left win top / left win

Note

The only change in behavior is on iOS, for positions that use right or bottom, which now render as they do on Android and the web.

Changelog:

[IOS] [FIXED] - backgroundPosition now honors right and bottom offsets

Test Plan:

Automated checks

Check Result
ConversionsTest.background_position_bottom_right Fails on main (top.has_value() and left.has_value() are true), passes with the fix
ConversionsTest.background_position_top_left_next_to_bottom_right Passes before and after
react_renderer_components_view_tests (Fantom react-common-tests) 105 / 105 pass
node ./scripts/clang-format.js --check on the changed files Pass
yarn cxx-api-validate (Doxygen 1.16.1) All 12 snapshots match

Manual (RNTester, iPhone 17 simulator, iOS 27.0)

BackgroundImage › Gradient with Background Position, with two extra boxes added locally ('right 8px bottom 8px' and [{bottom: 8, right: 8}], not part of this PR). The existing center, 25% 75% and right bottom boxes look the same before and after.

Before (main) After (this PR)
ios-before ios-after

BackgroundPosition defaults top and left to 0, and the props parser only
filled in bottom and right next to them. iOS reads top and left first, so
`backgroundPosition: 'right 8px bottom 8px'` drew the image at the top
left. Drop the default top when the value sets bottom, and the default left
when it sets right. An explicit top or left still wins, as on Android.
@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 Nov 13, 2026
@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 Nov 13, 2026
@github-actions

Copy link
Copy Markdown

Visual regression

1 visual difference(s) at 863d102f6819. Visual differences need maintainer approval. Review the report, then apply visual-change-approved.

ios button/default-styling/default: Changed, 38223 px (1.533%)

diff

Expected and Captured
Expected Captured
expected captured

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.

1 participant