Skip to content

fix(compiler): drop a namespace-qualified attribute selector - #449

Open
YevheniiKotyrlo wants to merge 4 commits into
nativewind:mainfrom
YevheniiKotyrlo:fix/attribute-selector-namespaces
Open

YevheniiKotyrlo wants to merge 4 commits into
nativewind:mainfrom
YevheniiKotyrlo:fix/attribute-selector-namespaces

Conversation

@YevheniiKotyrlo

@YevheniiKotyrlo YevheniiKotyrlo commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Problem

On main, the compiler ignores an attribute selector's namespace. [ns|att] represents only an attribute in the namespace ns (Selectors 4 §6.4), and no React Native prop is in a namespace, so the selector can match nothing. Instead the compiler reads the prop, and [ns|data-x="a"] matches dataSet={{ x: "a" }} where every browser matches nothing.

An undeclared prefix ([undeclared|data-x="a"]) makes the selector invalid, with the same outcome in a browser. lightningcss passes it through.

Solution

The attribute builder answers no query for a namespace-qualified attribute. Each of its two callers then drops the selector: the compound path and the :is()/:where() path. [att], [|att] and [*|att] are unchanged, since with no namespaced props they denote the same set.

Tests

Every case renders as Chromium 153, Firefox 155 and WebKit 26.6 do.

  • src/__tests__/compiler/selectors.test.tsx covers:
    • the rules [att], [|att], [*|att] and an undeclared prefix emit;
    • a declared prefix, beside a sibling rule that survives;
    • an ancestor's qualified attribute through the :is() path.
  • src/__tests__/native/attributes.test.tsx renders [|att], [*|att] and an undeclared prefix over a matching dataSet value.
  • On fix(compiler): read [class=…] from the prop the class list arrives on #448's head, 4 of the 64 cases in these files fail. Each of 4 mutations fails its own cases.

Verification

On Windows with Node 26:

  • yarn lint clean
  • yarn typecheck clean
  • yarn test --coverage: every failure also fails on main on this machine (the four babel cases)
  • yarn build clean
  • yarn example expo export --platform web exported

Merge order

This builds on #448's single attribute builder, which builds on #447, so #447 and #448 land first and this carries their commits until they do.

Base

Re-written on #448 (291ce56d), which sits on #447 and main (a5002c5).

@YevheniiKotyrlo

YevheniiKotyrlo commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor Author

No current device pair

Every build of my test app carries this change: the app's reading-direction code needs #459, which is built on #447, #448 and #449. So no build can show the before, and the description's tests are the evidence.

react-native-web writes a dataSet entry as a data-* attribute for every value
but null and undefined, so [data-x] matches data-x="false" in a browser. The
runtime treated false as absent there, as it rightly does for a boolean
prop, and compared an array value as no value, where the DOM holds its
comma-joined text.

HTML compares a dir value ASCII case-insensitively, so [dir="RTL"] matches
dir="rtl" unless the selector carries the s flag. The compiler emitted the
operand as written, and the direction condition never matched it. It now
folds the operand unless s is given.

Both folds share one ASCII lowercasing helper, which the runtime's i flag
also uses.
The compound path and the :is() / :where() path each built the same query
from an attribute selector, one with its own copy of the operator map. Both
now call one builder.
React Native carries the class list as className, so an attribute query
reading props.class answered false for every element, and [class~="x"],
[class*="x"] and [class] never matched. The attribute builder maps class to
className.
[ns|att] represents only an attribute in the namespace ns (Selectors 4
§6.4), and a React Native prop is in no namespace, so the selector can
match nothing. The compiler ignored the namespace and read the prop, so
[ns|data-x="a"] matched where every browser matches nothing. An undeclared
prefix is an invalid selector, with the same outcome. The attribute builder
now answers no query for a namespace-qualified attribute, and both of its
callers drop the selector; [|att] and [*|att] are unchanged.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant