Fix hairline borders not visible (#11527)

This commit is contained in:
Oleksii Bulenok
2026-08-31 16:11:43 +02:00
committed by GitHub
parent a086a2ebe0
commit 89c8e1cb70
4 changed files with 369 additions and 329 deletions
+22
View File
@@ -403,3 +403,25 @@ index 3e48dabc6fffc246fd0517ef5f3f2b7721115511..ea4ba5fdf359c513a5ca4f0e94492fac
- }
+ auto rootShadowNodeHolder = shadowTree.getCurrentRevision().rootShadowNode;
+ const auto* rootShadowNode = rootShadowNodeHolder.get();
diff --git a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm
--- a/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm
+++ b/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm
@@ -827,9 +827,17 @@
} else {
CGSize imageSize = image.size;
UIEdgeInsets imageCapInsets = image.capInsets;
+ // The stretchable middle is whatever lies between the cap insets. The image
+ // may be larger than capInsets + 1 (its size is ceil'd to whole points), so
+ // deriving the middle from the caps rather than assuming a 1pt band keeps
+ // the bottom/right caps at their true size. A phantom cap here makes the
+ // caps overflow sub-pixel-sized layers (e.g. hairline borders), and the
+ // squeezed mesh + nearest-neighbor filtering drops the stroke entirely.
CGRect contentsCenter = CGRect{
CGPoint{imageCapInsets.left / imageSize.width, imageCapInsets.top / imageSize.height},
- CGSize{(CGFloat)1.0 / imageSize.width, (CGFloat)1.0 / imageSize.height}};
+ CGSize{
+ (imageSize.width - imageCapInsets.left - imageCapInsets.right) / imageSize.width,
+ (imageSize.height - imageCapInsets.top - imageCapInsets.bottom) / imageSize.height}};
layer.contents = (id)image.CGImage;
layer.contentsScale = image.scale;
+19
View File
@@ -159,3 +159,22 @@ PR: https://github.com/facebook/react-native/pull/57483
Issue: https://github.com/react/react-native/issues/53450#issuecomment-3298157830
Bandaid fix taken from: https://github.com/react/react-native/commit/581d643a9e59fd88f93757f80194e1efd11bd0e5
## RCTViewComponentView.mm Patch - Hairline border strokes dropped at certain subpixel Y offsets on New Arch
Symptom: dividers built as `borderTopWidth: hairlineWidth` vanish on some screens and not
others, deterministically by the view's absolute subpixel Y. A background fill of the same
geometry always renders.
Cause: Fabric draws borders as a stretched 9-slice image. The consumer
(`RCTAddContourEffectToLayer`) hardcodes the stretchable middle as a 1pt band, which matched
the image the producer built until RN 0.81. facebook/react-native#54237 changed the image
size to `ceil(insets) + 1 + ceil(insets)` without updating that formula, so for fractional
(hairline) insets the labels no longer match the image: transparent filler is treated as a
rigid cap, and when squeezed into a one-pixel-tall layer the sampling can land on it instead
of the stroke - no line.
Fix: compute the middle from the cap insets (`size - caps`) instead of assuming 1pt.
Upstream issue: https://github.com/react/react-native/issues/58054 (repro:
https://github.com/abulenok/HairlineBorderRepro, fails identically on 0.86.0 and 0.87.0).
+326 -326
View File
File diff suppressed because it is too large Load Diff
@@ -2,7 +2,6 @@ import {createContext, useContext, useMemo} from 'react'
import {
type GestureResponderEvent,
type StyleProp,
StyleSheet,
View,
type ViewStyle,
} from 'react-native'
@@ -255,10 +254,10 @@ export function Divider({style}: ViewStyleProp) {
return (
<View
style={[
a.border_t,
t.atoms.border_contrast_low,
a.w_full,
a.my_sm,
{height: StyleSheet.hairlineWidth},
t.atoms.bg_contrast_100,
style,
]}
/>