Repository navigation
fix(compiler): answer an ancestor condition that names no class - #462
Open
YevheniiKotyrlo wants to merge 3 commits into
Open
YevheniiKotyrlo wants to merge 3 commits into
YevheniiKotyrlo wants to merge 3 commits into
Conversation
Contributor
Author
Re-cut onto main (a5002c5). Main keeps an attribute condition's kebab name, which React Native accepts as a prop.
YevheniiKotyrlo
force-pushed
the
fix/unnamed-ancestor-conditions
branch
from
October 11, 2026 12:35
b6fcc0f to
1b80844
Compare
This was referenced Oct 11, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Problem
A descendant combinator opens a container-query ref, and every later component of that compound writes its condition onto it. Only the
classarm attaches the ref to the rule, so an ancestor identified by a condition alone is discarded and the rule applies to every element the class names, in every state. The container queries each rule compiles to onmain:main[data-state="on"] .x[{"a":[["d","state","=","on"]]}]:hover .x[{"p":{"h":1}}]:disabled .x[{"a":[["a","disabled"]]}].g[data-state="on"] .x[{"a":[…],"n":"g:g"}].x:where([data-state="on"] *)[{"a":[["d","state","=","on"]]}]The last two rows are why this is a defect rather than a limit. The class arm attaches a named query and
parseIsWhereComponentsalready emits an unnamed one for the:where(… *)spelling, so two spellings of one selector disagree, and the one an author writes by hand fails open. A descendant combinator asks for an ancestor matching the whole compound to its left (Selectors 4 §14.1), so a condition in that compound is part of the match.Solution
The ref is attached when a condition is written, not only when a class names it.
attachContainerQueryadds a ref once, so a compound carrying both a class and a condition attaches one query and the named path compiles as before.The condition then resolves against the nearest container, as the
:where()spelling does. With no container above,testContainerQueryanswersfalse, so a selector the runtime cannot answer fails closed.Tests
src/__tests__/compiler/ancestor-conditions.test.ts: each row above, a chain of two classless ancestors, a named and a classless ancestor together, a multi-class compound yielding one query, the combinator compiling to what:where()does, a compound on one element staying the subject's, and an unanswerable combinator still dropping the rule.src/__tests__/native/ancestor-conditions.test.tsx, through the runtime:[data-state="on"]and:disabledancestors following the container's props both waysOn
main, 14 of the 22 cases in the two files fail; all pass here. Removing the attach from the hover, attribute and disabled arms fails their own cases.Every ancestor-state test in the suite names a class, because that is what Tailwind emits:
group-hover:compiles to.group:hover .x, which was already correct. The classless form is what an author writes in a plain stylesheet.Verification
On Windows with Node 26:
yarn lintcleanyarn typecheckcleanyarn test --maxWorkers=2 --coverage: 1353 passed, 4 failed. The 4 are the babel cases that also fail onmainon this machine.yarn buildcleanyarn example expo export --platform webexportedNo existing issue tracks this. I searched for
ancestor attribute selector,data- descendant,hover descendant,group-hoverandparent hover.Device evidence, before and after: #462 (comment)
Known limits
An unnamed query resolves the nearest ancestor that established a container, not any ancestor. That under-matches, as
:where(… *)already does. Going further needs every element to be addressable, and the stylesheet is class-keyed: an element carrying no class the sheet names registers nothing.Independent of #463, which decides the order of an ancestor chain at runtime. They touch no common file.
Merge order
It shares lines with #448 (
compiler/selector-builder.ts), #459 (compiler/selector-builder.ts); whichever lands second rebases.Base
Re-written on
main(a5002c5).