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
YevheniiKotyrlo
left a comment
There was a problem hiding this comment.
Confirming this from production (Expo SDK 55 · RN 0.83.6 · Tailwind v4) — border-s-* / border-e-* are invisible on native here (calendar column rules + a tree-view indent guide). Thanks for tackling it at the parser layer.
I built fix-logical-border-props locally and compiled the logical-border matrix through it. Longhands and literal shorthands are fixed. One gap the new logical-borders.test.ts doesn't cover: var()-valued border-inline shorthands still emit borderInline*.
Compiled through this branch (compile('.x { … }').stylesheet().s):
| 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 the parseBorderInline* parsers never run — 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.
I have a fix ready on top of this branch. propertyRename can't do it (a shorthand needs a 1→2 expansion, not a 1→1 rename), so it goes in the same parseUnparsedDeclaration, mirroring your parseBorderInline* on the parsed path. The two broken cases now expand to borderStart/End*, and the logical-borders suite is 15/15 (your 13 + 2 new).
declarations.ts diff + the 2 tests
+// Logical-border SHORTHANDS have no RN equivalent either; when var()-valued
+// they reach the unparsed path (the parsed parseBorderInline* never run) and
+// propertyRename only maps the longhands. Expand each to its RTL-aware
+// start/end props, sharing the runtime value.
+const inlineShorthandExpansion: Record<string, string[]> = {
+ "border-inline-color": ["border-start-color", "border-end-color"],
+ "border-inline-width": ["border-start-width", "border-end-width"],
+};
// …inside parseUnparsedDeclaration, right after the propertyRename block:
+ const shorthandExpansion = inlineShorthandExpansion[property];
+ if (shorthandExpansion) {
+ const value = parseUnparsed(declaration.value.value, builder, property);
+ for (const target of shorthandExpansion) {
+ builder.descriptorProperty = target;
+ builder.addDescriptor(target, value);
+ }
+ return;
+ }test("border-inline-color with var()", () => {
expect(getRule("border-inline-color: hsl(var(--primary));").rule).toStrictEqual([
{ s: [1, 1], dv: 1, d: [
[[{}, "hsl", [{}, "var", "primary", 1]], "borderStartColor", 1],
[[{}, "hsl", [{}, "var", "primary", 1]], "borderEndColor", 1],
] },
]);
});
test("border-inline-width with var()", () => {
expect(getRule("border-inline-width: var(--w);").rule).toStrictEqual([
{ s: [1, 1], dv: 1, d: [
[[{}, "var", "w", 1], "borderStartWidth", 1],
[[{}, "var", "w", 1], "borderEndWidth", 1],
] },
]);
});Happy to open this as a PR into fix-logical-border-props, or you can grab the diff — whichever you prefer. One rarer edge is deliberately left out: the full border-inline: 2px solid var() shorthand (still emits borderInline; it needs runtime shorthand splitting, and Tailwind never emits it) — a small follow-up if you want it.
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