From 2e03f93a710e19e89c98558e2c6c339bdd07b0fb Mon Sep 17 00:00:00 2001 From: Gregory Moskaliuk Date: Sat, 24 Oct 2026 11:20:25 +0900 Subject: [PATCH 1/2] fix(ios): backport clear text background omission to 0.88 Backport the already-landed background attribute fix from #59207. Omitting the default clear attribute also restores the Japanese IME composition underline in the isolated native input comparison. (cherry picked from commit 0bd33261d1e519f319c3225247879273c5a2a709) --- .../react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm index 95ff5a0d180e..927c3c296432 100644 --- a/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm +++ b/packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm @@ -205,7 +205,8 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex attributes[NSForegroundColorAttributeName] = effectiveForegroundColor; } - if (textAttributes.backgroundColor || !isnan(textAttributes.opacity)) { + // A clear background draws nothing, but TextKit still computes and fills its rects on every draw. + if (textAttributes.backgroundColor && textAttributes.backgroundColor != clearColor()) { attributes[NSBackgroundColorAttributeName] = RCTEffectiveBackgroundColorFromTextAttributes(textAttributes); } From b2415a5db9a19811e3f52741f9a348dcd62211a4 Mon Sep 17 00:00:00 2001 From: riottt <58056595+riottt@users.noreply.github.com> Date: Sat, 24 Oct 2026 11:57:13 +0900 Subject: [PATCH 2/2] test(ios): cover clear text background conversion --- .../Tests/Text/RCTAttributedTextUtilsTest.mm | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/packages/react-native/React/Tests/Text/RCTAttributedTextUtilsTest.mm b/packages/react-native/React/Tests/Text/RCTAttributedTextUtilsTest.mm index a02622d5d7d7..371aea5a2e95 100644 --- a/packages/react-native/React/Tests/Text/RCTAttributedTextUtilsTest.mm +++ b/packages/react-native/React/Tests/Text/RCTAttributedTextUtilsTest.mm @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -133,6 +134,70 @@ - (void)testTextAlignmentStartAndEndResolveWithLayoutDirection XCTAssertEqual(NSTextAlignmentFromTextAlign(@"end", LayoutDirection::RightToLeft), NSTextAlignmentLeft); } +- (void)testDefaultClearBackgroundIsOmitted +{ + TextAttributes textAttributes = TextAttributes::defaultTextAttributes(); + + NSDictionary *attributes = RCTNSTextAttributesFromTextAttributes(textAttributes); + + XCTAssertNil(attributes[NSBackgroundColorAttributeName]); + XCTAssertEqualObjects(attributes[NSForegroundColorAttributeName], [UIColor colorWithRed:0 green:0 blue:0 alpha:1]); +} + +- (void)testOpacityDoesNotAddBackgroundColor +{ + TextAttributes textAttributes; + textAttributes.opacity = 0.5; + + NSDictionary *attributes = RCTNSTextAttributesFromTextAttributes(textAttributes); + + XCTAssertNil(attributes[NSBackgroundColorAttributeName]); + XCTAssertEqualObjects(attributes[NSForegroundColorAttributeName], [UIColor colorWithWhite:0 alpha:0.5]); +} + +- (void)testDefaultClearBackgroundWithOpacityIsOmitted +{ + TextAttributes textAttributes = TextAttributes::defaultTextAttributes(); + textAttributes.opacity = 0.5; + + NSDictionary *attributes = RCTNSTextAttributesFromTextAttributes(textAttributes); + + XCTAssertNil(attributes[NSBackgroundColorAttributeName]); + XCTAssertEqualObjects(attributes[NSForegroundColorAttributeName], [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5]); +} + +- (void)testVisibleBackgroundColorPreservesAlphaWithOpacity +{ + TextAttributes textAttributes; + textAttributes.backgroundColor = colorFromComponents({.red = 1, .alpha = 0.5, .colorSpace = ColorSpace::sRGB}); + + NSDictionary *attributes = RCTNSTextAttributesFromTextAttributes(textAttributes); + + XCTAssertEqualObjects(attributes[NSBackgroundColorAttributeName], [UIColor colorWithRed:1 green:0 blue:0 alpha:0.5]); + + textAttributes.opacity = 0.5; + attributes = RCTNSTextAttributesFromTextAttributes(textAttributes); + + XCTAssertEqualObjects(attributes[NSBackgroundColorAttributeName], [UIColor colorWithRed:1 green:0 blue:0 alpha:0.25]); +} + +- (void)testForegroundColorPreservesAlphaWithOpacity +{ + TextAttributes textAttributes = TextAttributes::defaultTextAttributes(); + textAttributes.foregroundColor = colorFromComponents({.blue = 1, .alpha = 0.5, .colorSpace = ColorSpace::sRGB}); + + NSDictionary *attributes = RCTNSTextAttributesFromTextAttributes(textAttributes); + + XCTAssertNil(attributes[NSBackgroundColorAttributeName]); + XCTAssertEqualObjects(attributes[NSForegroundColorAttributeName], [UIColor colorWithRed:0 green:0 blue:1 alpha:0.5]); + + textAttributes.opacity = 0.5; + attributes = RCTNSTextAttributesFromTextAttributes(textAttributes); + + XCTAssertNil(attributes[NSBackgroundColorAttributeName]); + XCTAssertEqualObjects(attributes[NSForegroundColorAttributeName], [UIColor colorWithRed:0 green:0 blue:1 alpha:0.25]); +} + - (void)testSamenessOfEmptyAttributedStrings { NSAttributedString *attributedString1 = [[NSAttributedString alloc] initWithString:@""];