-
-
Notifications
You must be signed in to change notification settings - Fork 24
Closed
Labels
bugSomething isn't workingSomething isn't workingrustPull requests that update rust codePull requests that update rust code
Description
Summary
- functional pseudo selectors generated by the extractor drop the required parentheses when another selector is nested (e.g.
_not={{ _lastOfType: {} }}becomes:not:last-of-type) - CSS parsers reject the emitted selector, so builds that rely on Lightning CSS / PostCSS currently fail
- the issue impacts every pseudo selector that requires parentheses such as
_not,_is,_where,_has,_nthChild,_lang, etc., preventing us from styling with those helpers
Steps to Reproduce
- Render any component with the pseudo helper, for example:
<Box _not={{ _lastOfType: { color: 'tomato', }, }} />
- Run the extractor via the build/dev toolchain (e.g.
pnpm devinapps/landing). - Inspect the generated CSS or let Lightning CSS validate it – the output contains a selector fragment
&:not:last-of-type.
Expected Behavior
- Nested pseudo selectors should produce
&:not(:last-of-type)so that the child selector is wrapped in parentheses.
Actual Behavior
- The extractor yields
&:not:last-of-type(missing parentheses) which is rejected by Lightning CSS withExpected "(" after ":not"and by browsers because it is not valid CSS.
Root Cause Analysis
- When
_not(or any pseudo helper) is processed, we recurse viaextract_style_from_expressionand chain the current selector with the new one (libs/extractor/src/extractor/extract_style_from_expression.rs:249). - Chaining delegates to
StyleSelector::from([base, next]), which simply concatenates the pieces usingSelectorSeparator(libs/css/src/style_selector.rs:158). - The formatter emits
&:not:last-of-typebecauseSelectorSeparatorcan only return:/::/ empty (libs/css/src/selector_separator.rs:22), so there is no opportunity to insert parentheses before appending the new selector. - No special casing exists for functional pseudo classes (e.g.
:not,:is,:where,:has,:nth-child,:lang), so every nested use results in invalid selectors.
Impact
- Any component relying on functional pseudo selectors fails to compile, so patterns like
_not,_is,_where,_has,_nthChild,_nthLastOfType,_nthCol,_lang,_dir, etc. cannot be used. - Docs currently encourage these helpers, but attempting to use them blocks builds in Next.js / Vite because Lightning CSS throws a syntax error.
- There is no workaround within the design system other than inlining raw CSS selectors by hand, defeating the zero-install goal.
Suggested Fix
- Track functional pseudo selectors and wrap the chained selector string in parentheses whenever the base selector ends with one of those functions (e.g.
format!("{base}({body})")). - Alternatively, represent selectors as structured AST nodes instead of raw strings so that formatting can distinguish between pseudo classes and functions before serialisation.
- ref: rust-cssparser
- Add snapshot/unit coverage for
_not+_lastOfType,_iswith multiple selectors,_nthChildwith numeric arguments, etc., to prevent regressions.
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't workingrustPull requests that update rust codePull requests that update rust code