Skip to content

fix: avoid wrapping pseudo-element selectors in :is() during nesting compilation#1156

Open
donlion wants to merge 1 commit intoparcel-bundler:masterfrom
donlion:fix/nesting-pseudo-element-is-wrapper
Open

fix: avoid wrapping pseudo-element selectors in :is() during nesting compilation#1156
donlion wants to merge 1 commit intoparcel-bundler:masterfrom
donlion:fix/nesting-pseudo-element-is-wrapper

Conversation

@donlion
Copy link
Copy Markdown

@donlion donlion commented Feb 19, 2026

Summary

When compiling nested selectors like .parent & where the parent selector contains a pseudo-element (e.g., .element::after), the code was incorrectly wrapping the parent in :is(), producing invalid CSS.

Input:

.element::after {
  .parent & {
    color: red;
  }
}

Before (incorrect):

.parent :is(.element:after) {
  color: red;
}

After (correct):

.parent .element:after {
  color: red;
}

Root Cause

The is_simple() function returns false for selectors with pseudo-elements because they internally contain a Combinator::PseudoElement component. This caused the nesting serialization to fall back to using :is(), which cannot contain pseudo-elements per CSS specification.

Fix

Added a check in serialize_nesting to detect when the parent selector contains a pseudo-element. In that case, we serialize the selector directly instead of wrapping it in :is().

Related Issues

Test Plan

  • Added test case for the specific scenario
  • All existing tests pass (cargo test)

…compilation

When compiling nested selectors like `.parent &` where the parent selector
contains a pseudo-element (e.g., `.element::after`), the code was incorrectly
wrapping the parent in `:is()`, producing invalid CSS like `.parent :is(.element:after)`.

The :is() pseudo-class cannot contain pseudo-elements per CSS specification.
This fix adds a check to serialize pseudo-element selectors directly instead.
@devongovett
Copy link
Copy Markdown
Member

what happens if there are multiple selectors?

.foo, .element::after {
  .parent & {
    color: red;
  }
}

doing this without :is is really difficult if not impossible in some cases to do correctly.

@donlion
Copy link
Copy Markdown
Author

donlion commented Mar 9, 2026

Good point, I’ll see if I can come up with something.
Would it be terrible to compile to this?

.parent .foo, .parent .element::after {
}

@yisibl
Copy link
Copy Markdown
Contributor

yisibl commented Mar 9, 2026

Essentially, the current CSS specification does not permit nesting & within pseudo-elements, which still requires addressing the case mentioned in this comment. #975 (comment)

@yisibl
Copy link
Copy Markdown
Contributor

yisibl commented Mar 9, 2026

We need to ensure that the CSS in this WPT test case, after being compiled by lightningcss, still displays entirely in green.
http://wpt.live/css/css-nesting/nesting-basic.html

@donlion
Copy link
Copy Markdown
Author

donlion commented Mar 9, 2026

Essentially, the current CSS specification does not permit nesting & within pseudo-elements, which still requires addressing the case mentioned in this comment. #975 (comment)

Aha! That's news to me.
I suppose it's a matter of opinion on wether it should work and compile invalid css to valid css.
But I think it’s very valid to decide, that it shouldn't :)

What’s your thoughts?

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.

Ligthning generate :is with pseudo element

3 participants