Skip to content

A value read through var() that the property can't take still applies: border: var(--b) over --b: 1px, solid draws a border, and border-inline-width over red keeps an earlier width #552

Open

Description

@YevheniiKotyrlo

Before submitting a new issue

  • I tested using the latest version of the library, as the bug might be already fixed.
  • I tested using a supported version of react native.
  • I checked for possible duplicate issues, with possible answers.

Bug summary

In CSS a var() whose value doesn't fit the property's grammar makes the declaration invalid at computed-value time (css-variables-1 §3): the property is unset, and an earlier rule's value for it no longer applies. The shorthand readers get both halves of that wrong, on the folded route and with inlineVariables: false alike:

  1. A comma list is read as a space list. border and border-inline-width have no comma in their grammar, yet border: var(--b) over --b: 1px, solid draws a 1px solid border, and border-inline-width: var(--w) over --w: 1px, 2px applies 1 and 2, as if the value were written 1px 2px.
  2. A refused value leaves the earlier one. border-inline-width: var(--w) and border-block-width: var(--w) over --w: red are refused, but an earlier rule's 5px stays, where the property should be unset.
folded .a { --b: 1px, solid; border: var(--b); }
   => {"borderWidth":1,"borderStyle":"solid"}
kept   .a { --b: 1px, solid; border: var(--b); }
   => {"borderWidth":1,"borderStyle":"solid"}
folded .earlier { border-inline-width: 5px; } .a { --w: 1px, 2px; border-inline-width: var(--w); }
   => {"borderStartWidth":1,"borderEndWidth":2}
kept   .earlier { border-inline-width: 5px; } .a { --w: 1px, 2px; border-inline-width: var(--w); }
   => {"borderStartWidth":1,"borderEndWidth":2}
folded .earlier { border-inline-width: 5px; } .a { --w: red; border-inline-width: var(--w); }
   => {"borderStartWidth":5,"borderEndWidth":5}
kept   .earlier { border-inline-width: 5px; } .a { --w: red; border-inline-width: var(--w); }
   => {"borderStartWidth":5,"borderEndWidth":5}
folded .earlier { border-block-width: 5px; } .a { --w: red; border-block-width: var(--w); }
   => {"borderTopWidth":5,"borderBottomWidth":5}
kept   .earlier { border-block-width: 5px; } .a { --w: red; border-block-width: var(--w); }
   => {"borderTopWidth":5,"borderBottomWidth":5}
  • shorthandHandler (src/native/styles/shorthands/_handler.ts) runs args = args.flat() on the resolved value before matching it against the shorthand's patterns, so a comma-separated list's items match as one space-separated list.
  • logicalWidth (src/native/styles/shorthands/logical-border-width.ts) returns undefined for a value that isn't one or two finite non-negative numbers, and undefined sets nothing, so the earlier rule's width stays; returning the two keys as null would unset them.

I've filed them together since they're the same rule failing in two readers that share one harness. #547 keeps a list's separator when it's read through a variable, so it makes case 1 a refusal for border-inline-width, which then lands in case 2.

Library version

main at a37903f (0.0.0-nightly.a37903f)

Environment info

Windows 11 10.0.26300, Node 26.12.1, Yarn 4.9.2, react-native 0.86.3 (the repository's dev dependency).
No app or native build is involved: the output comes from the repository's jest setup.

Steps to reproduce

  1. Clone the reproducer branch, which is main at a37903f with one file added, src/__tests__/native/comma-list-in-a-variable.test.tsx, and install:
    git clone --branch repro/552-comma-list-read-as-a-space-list https://github.com/YevheniiKotyrlo/react-native-css
    cd react-native-css
    yarn install
  2. Run yarn jest src/__tests__/native/comma-list-in-a-variable.test.tsx and read the logged lines.

src/__tests__/native/comma-list-in-a-variable.test.tsx:

import { StyleSheet } from "react-native";

import { render, screen } from "@testing-library/react-native";
import { View } from "react-native-css/components/View";
import { registerCSS, testID } from "react-native-css/jest";

jest.mock("../../native/reanimated", () => ({
  animatedComponentFamily: (component: unknown) => component,
}));

test.each([
  ".a { --b: 1px, solid; border: var(--b); }",
  ".earlier { border-inline-width: 5px; } .a { --w: 1px, 2px; border-inline-width: var(--w); }",
  ".earlier { border-inline-width: 5px; } .a { --w: red; border-inline-width: var(--w); }",
  ".earlier { border-block-width: 5px; } .a { --w: red; border-block-width: var(--w); }",
])("%s", (css) => {
  for (const inlineVariables of [true, false]) {
    registerCSS(css, inlineVariables ? {} : { inlineVariables: false });
    render(<View testID={testID} className="earlier a" />);
    console.log(
      `${inlineVariables ? "folded" : "kept  "} ${css}\n   => ${JSON.stringify(StyleSheet.flatten(screen.getByTestId(testID).props.style))}`,
    );
    screen.unmount();
  }
});

Expected: border: var(--b) over 1px, solid draws no border; border-inline-width: var(--w) over 1px, 2px or red sets neither borderStartWidth nor borderEndWidth, so the earlier 5px no longer applies; the same for border-block-width.

Actual: the border is drawn 1px solid, 1px, 2px applies 1 and 2, and red leaves the earlier 5px.

The run's output on main at a37903f, Windows 11, Node 26.12.1
  console.log
    folded .a { --b: 1px, solid; border: var(--b); }
       => {"borderWidth":1,"borderStyle":"solid"}

      at log (src/__tests__/native/comma-list-in-a-variable.test.tsx:20:13)

  console.log
    kept   .a { --b: 1px, solid; border: var(--b); }
       => {"borderWidth":1,"borderStyle":"solid"}

      at log (src/__tests__/native/comma-list-in-a-variable.test.tsx:20:13)

  console.log
    folded .earlier { border-inline-width: 5px; } .a { --w: 1px, 2px; border-inline-width: var(--w); }
       => {"borderStartWidth":1,"borderEndWidth":2}

      at log (src/__tests__/native/comma-list-in-a-variable.test.tsx:20:13)

  console.log
    kept   .earlier { border-inline-width: 5px; } .a { --w: 1px, 2px; border-inline-width: var(--w); }
       => {"borderStartWidth":1,"borderEndWidth":2}

      at log (src/__tests__/native/comma-list-in-a-variable.test.tsx:20:13)

  console.log
    folded .earlier { border-inline-width: 5px; } .a { --w: red; border-inline-width: var(--w); }
       => {"borderStartWidth":5,"borderEndWidth":5}

      at log (src/__tests__/native/comma-list-in-a-variable.test.tsx:20:13)

  console.log
    kept   .earlier { border-inline-width: 5px; } .a { --w: red; border-inline-width: var(--w); }
       => {"borderStartWidth":5,"borderEndWidth":5}

      at log (src/__tests__/native/comma-list-in-a-variable.test.tsx:20:13)

  console.log
    folded .earlier { border-block-width: 5px; } .a { --w: red; border-block-width: var(--w); }
       => {"borderTopWidth":5,"borderBottomWidth":5}

      at log (src/__tests__/native/comma-list-in-a-variable.test.tsx:20:13)

  console.log
    kept   .earlier { border-block-width: 5px; } .a { --w: red; border-block-width: var(--w); }
       => {"borderTopWidth":5,"borderBottomWidth":5}

      at log (src/__tests__/native/comma-list-in-a-variable.test.tsx:20:13)

PASS src/__tests__/native/comma-list-in-a-variable.test.tsx
  √ .a { --b: 1px, solid; border: var(--b); } (31 ms)
  √ .earlier { border-inline-width: 5px; } .a { --w: 1px, 2px; border-inline-width: var(--w); } (5 ms)
  √ .earlier { border-inline-width: 5px; } .a { --w: red; border-inline-width: var(--w); } (3 ms)
  √ .earlier { border-block-width: 5px; } .a { --w: red; border-block-width: var(--w); } (2 ms)

Test Suites: 1 passed, 1 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        1.684 s, estimated 2 s
Ran all test suites matching /src\\__tests__\\native\\comma-list-in-a-variable.test.tsx/i.

Reproducible example repository

https://github.com/YevheniiKotyrlo/react-native-css/tree/repro/552-comma-list-read-as-a-space-list

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions