Skip to content

Parse CSS Nesting's leading-combinator shorthand in nested rules - #612

Closed
davesnx wants to merge 3 commits into
grammar-font-face-metricsfrom
parser-nested-relative-selector
Closed

davesnx wants to merge 3 commits into
grammar-font-face-metricsfrom
parser-nested-relative-selector

Conversation

@davesnx

@davesnx davesnx commented Sep 19, 2026

Copy link
Copy Markdown
Owner

A nested rule whose prelude starts with a combinator (.parent { > .child {} }, + .sib {}, ~ .sib {}) failed with Parse error while reading token '>', although CSS Nesting defines it as & > .child; only a :has(> img) payload accepted a leading combinator. Rules inside another rule's block, or an at-rule's block, now parse their prelude with parse_nested_selector_list, which reads an item starting with a combinator as a RelativeSelector. Selector_nesting.compute_new_prefix rewrites it into the explicit-& shape before substitution, so both spellings produce the same AST, the same class and the same CSS. The root of [%css], [%styled.global] and a raw stylesheet keeps rejecting a leading combinator, because there is no parent to resolve against.

.parent { > .child { color: red } }   -> .parent > .child{color:red;}   (same class as `& > .child`)
.parent { + .sib { color: red } }     -> .parent + .sib{color:red;}
> .a { color: red }                   -> parse error at the root, unchanged

[%styled.global] also rejects a leading combinator inside an at-rule block with no enclosing style rule (@media print { > .a {} }) with the same "no parent selector to resolve against" error a parentless & gives. Before, that input compiled and shipped a literal unresolved & in the extracted CSS.

Evidence at b3ba860:

  • Parser_test.re: 8 shorthand cases red before and green after; 2 cases guard the root-level rejection.
  • New cram test packages/ppx/test/css-support/nested-relative-selectors.t: each shorthand form and its & spelling render to an identical class and CSS.
  • New snapshot reason-styled-global2-top-level-relative-selector-error.t: the at-rule case errors with the caret on > .a; it compiled silently before.
  • make format-check, make build, make test: all exit 0.

Risk: two-way door. Only previously failing inputs parse, plus the new rejection of a [%styled.global] input that was silently broken; a revert restores both.

@vercel

vercel Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
styled-ppx Ignored Ignored Preview Sep 23, 2026 6:41am UTC

Request Review

@davesnx
davesnx force-pushed the grammar-font-face-metrics branch from ca959c5 to 898109f Compare September 22, 2026 12:46
@davesnx
davesnx force-pushed the parser-nested-relative-selector branch 3 times, most recently from 68fe3df to d4cacfe Compare September 23, 2026 06:11
`.parent { > .child {} }` means `.parent { & > .child {} }`. Rules inside
another rule's block now parse their prelude with
`parse_nested_selector_list`, which reads an item that starts with a
combinator as a `RelativeSelector`; the root of `[%css]`,
`[%styled.global]` and a raw stylesheet keeps rejecting it because there
is no parent to resolve against. `Selector_nesting.compute_new_prefix`
rewrites the relative selector into the explicit-`&` shape first, so both
spellings produce the same AST and the same class.

`[%styled.global]` also rejects a leading combinator inside an at-rule
block with no enclosing style rule through `needs_parent_selector`, with
the same error a parentless `&` gives, instead of shipping an unresolved
`&` in the extracted CSS.
@davesnx
davesnx force-pushed the grammar-font-face-metrics branch from a0cbd06 to 1c6b4f6 Compare September 23, 2026 06:39
@davesnx
davesnx force-pushed the parser-nested-relative-selector branch from d4cacfe to 42eaf9c Compare September 23, 2026 06:39
@davesnx davesnx closed this Sep 23, 2026
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