fix: emit React Native border-start/end props for border-inline styles - #379
fix: emit React Native border-start/end props for border-inline styles#379danstepanov wants to merge 1 commit into
Conversation
border-inline-start-color and friends compiled to props like borderInlineStartColor that React Native does not support, so every border-s-* and border-e-* utility was a silent no-op on native. Rename them to the RTL-aware border-start-* and border-end-* props in both the parsed and unparsed paths, fix the border-inline shorthands, and drop per-side border styles (with a compiler warning when the style is not solid, since solid matches the native default). Dev builds now print compiler warnings from the metro transformer so declarations without a native equivalent are visible while building. Closes #378
There was a problem hiding this comment.
Confirming from production (Expo SDK 55 · RN 0.83.6 · Tailwind v4) — border-s-* / border-e-* were silently dropped on native before this. Thanks for fixing it at the parser layer.
I compiled the full logical-border matrix through fix-logical-border-props. Longhands and literal shorthands are handled. One gap remains: var()-valued border-inline shorthands still emit borderInline*.
| declaration | this branch emits |
|---|---|
border-inline-start-color: hsl(var(--p)) — longhand |
✅ borderStartColor |
border-inline-color: red blue — literal shorthand |
✅ borderStartColor + borderEndColor |
border-inline-color: hsl(var(--p)) — var shorthand |
❌ borderInlineColor |
border-inline-width: var(--w) — var shorthand |
❌ borderInlineWidth |
A var() routes the shorthand to parseUnparsedDeclaration, where propertyRename maps only the longhands and parseBorderInline* never runs — so border-inline-color / -width fall through unrenamed. Not hypothetical: Tailwind v4's border-x-<token> emits exactly border-inline-color: var(...) (v4 theme colors are CSS vars), so a themed border-x-primary is still a native no-op after this PR.
propertyRename can't cover it — a shorthand needs a 1→2 expansion, not a 1→1 rename — so the fix goes in the same parseUnparsedDeclaration, mirroring your parseBorderInline*. I've opened it as #393, stacked on this branch so the diff is just the delta (+2 cases, logical-borders at 15/15). Merge it in, cherry-pick the commit, or tell me to retarget — whichever's easiest.
Out of scope by design: the full border-inline: 2px solid var() form (still emits borderInline; needs runtime shorthand splitting, and Tailwind never emits it) — a small follow-up if you want it.
`border-block-*` reaches React Native under keys it has no attribute for, so
the declaration compiles, renders and paints nothing. Measured through this
branch's own compiler and asserted against the props a rendered `View`
receives:
border-block: 2px solid red -> borderBlockColor, borderBlockWidth,
borderBlockStyle
border-block-width: 2px -> borderBlockWidth
border-block-start-width: 2px -> borderBlockStartWidth
border-block-style: dashed -> borderBlockStyle
border-block: var(--b) -> borderBlock: [2, "dashed", "red"]
React Native's support here is not uniform, which is what makes the defect
hard to see. The three block COLOURS are real props — `borderBlockColor`,
`borderBlockStartColor` and `borderBlockEndColor` are in
`ReactNativeStyleAttributes`, in `BaseViewConfig.android.js`, in
`BaseViewConfig.ios.js` and in `ViewStyle`. The block WIDTHS are in
`BaseViewConfig.ios.js` and nowhere else, so an emitted `borderBlockWidth`
paints on iOS Fabric and is dropped on Android and on the old architecture.
No per-edge border STYLE exists at any layer on either platform.
So the colours are kept as they are, the widths map to the physical edges
every platform reads, and the styles drop the way the inline axis already
drops them. `direction` never flips the block axis, so block-start is the top
edge and block-end the bottom one on every platform, which makes the mapping
exact rather than an approximation.
The live trigger is Tailwind: `border-y-1` compiled to
`{ borderBlockWidth: 1, borderBlockStyle: "solid" }`, two keys React Native
ignores, so the utility drew nothing on Android — the block-axis twin of the
`border-x-*` bug nativewind#379 fixed. `src/__tests__/vendor/tailwind/borders.test.tsx`
asserted those two dead keys and passed while broken, exactly as nativewind#378
describes for the inline axis; it now asserts `borderTopWidth` /
`borderBottomWidth`.
Also on the unparsed path: `border-block-color: var(--a) var(--b)` put the
whole two-value list into one key, and `border-block-end-style` was missing
from the parser table so it warned as an unsupported property while
`border-block-start-style` silently emitted a dead key. Both now behave like
their inline-axis twins.
`parseBorderInlineStyle` becomes `parseUnsupportedEdgeStyle` and serves all
six per-edge style longhands: the decision it encodes — which per-edge styles
React Native can express, and how a dropped one is reported — is the same on
both axes, and two copies of it could answer differently.
The two planes are independently load-bearing, by measurement. Removing the
`borderBlock` runtime handler leaves every compiler test green and turns three
native ones red; pointing that handler at `borderBlockWidth` does the same.
A dead key is invisible in the IR, where it looks exactly like a real
declaration, so the assertion has to be made against the props the component
received.
The new sweep is derived rather than restated: it generates all 24
`border-{inline,block}[-start|-end][-width|-style|-color]` properties, drives
each through both the literal and the var() route, and asserts every rendered
key is one React Native declares. The census of real keys carries
`satisfies readonly (keyof ViewStyle)[]`, so a name React Native does not
declare cannot be added to it to make a dead key pass, and a name React Native
drops later turns the type-check red.
Closes #378, follow up to nativewind/nativewind#1737.
What
border-inline-start-color, border-inline-end-color, and the width and shorthand variants compiled to props like borderInlineStartColor that React Native does not have, so every border-s-* and border-e-* utility (and border-x-, which Tailwind v4 also emits as border-inline-) was a silent no-op on native.
border-s-*andborder-e-*don't work nativewind#1737 report).Tests