Skip to content

fix(CSS): backgroundImage gradient parsing and web serialization - #3

Closed
MatiPl01 wants to merge 3 commits into
Titozzz:claude/reanimated-backgroundimage-support-as2wrlfrom
software-mansion:@matipl01/background-image-gradient-fixes
Closed

fix(CSS): backgroundImage gradient parsing and web serialization#3
MatiPl01 wants to merge 3 commits into
Titozzz:claude/reanimated-backgroundimage-support-as2wrlfrom
software-mansion:@matipl01/background-image-gradient-fixes

Conversation

@MatiPl01

Copy link
Copy Markdown

Fixes found while reviewing software-mansion/react-native-reanimated#10193, targeted at that branch so they land as part of it.

Parser - a style that renders in a static backgroundImage must not start throwing once it is animated, since the processor runs every frame. radial-gradient(50%, ...) and circle 50% are invalid CSS but React Native renders them, so they are accepted rather than rejected. linear-gradient(to<TAB>right, ...) parses too - the direction check hard-coded a single space.

Color stops - the object syntax now rejects a transition hint that is not between two color stops, matching the CSS string path. Colors CSSColor cannot represent (PlatformColor, DynamicColorIOS) raise the same error boxShadow already does, instead of being serialized back as a null color and drawn as a hint.

Web serialization - radial positions emit both axes: { top: '10%' } produced at top 10%, which Chrome rejects outright, dropping the whole background-image with it, and { left: '10%' } meant x=0%, y=10% where native means x=10%, y=50%. Circle sizes degrade to circle max(x,y)px, or to the equivalent ellipse when a percentage radius makes it inexpressible.

<Animated.View
  style={{
    width: 84,
    height: 84,
    backgroundImage: [
      {
        type: 'radial-gradient',
        position: { top: '10%' },
        colorStops: [{ color: 'red' }, { color: 'blue' }],
      },
    ],
  }}
/>

🖼️ placeholder - upload the web before/after screenshot here

Docs - radial sizes and positions do interpolate when their units and edges match; only the corner keyword, shape and extent keyword must be identical. The flip point differs per mode.

Testing

1505 jest tests, both typechecks, eslint and the formatters pass. Every serialized string was checked against CSS.supports in headless Chrome, and the parser and color-stop changes were exercised on the iOS simulator.

Left for a separate decision: processBackgroundImageObjects forwards position unvalidated, so an unrecognised side is silently dropped and, because canInterpolateTo compares side names, a dropped side turns a smooth animation into a discrete step. Closing that adds new throws.

to-only keyframes for backgroundImage still resolve to the interpolator default rather than the element's own gradient - a separate defect in the property path lookup, fixed in software-mansion/react-native-reanimated#10253.

@MatiPl01

Copy link
Copy Markdown
Author

Closing for now - opened this a bit early. The fixes are parked on our side and I'll follow up on software-mansion#10193 itself. Sorry for the noise.

@MatiPl01 MatiPl01 closed this Aug 13, 2026
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