stylelint icon indicating copy to clipboard operation
stylelint copied to clipboard

Add a rule to disallow unmatched pseudo-elements in (desugared) selectors

Open bschlenk opened this issue 11 months ago • 1 comments

What is the problem you're trying to solve?

It's invalid to use native css nesting under pseudo-elements, because nesting desugars to :is() and :is() can't select pseudo-elements. It'd be nice to have a stylelint rule to ensure this type of nesting isn't accidentally introduced.

For example, this isn't valid:

.something::before {
  color: blue;

  .parent:hover & {
    color: red;
  }
}

/* becomes .parent:hover :is(.something::before), which is discarded by the browser */

Instead, it would have to be written as

.something::before {
  color: blue;
}

.parent:hover .something::before {
  color: red;
}

However, nesting media queries is valid (as long as there aren't any further nested selectors within):

.something::before {
  color: blue;

  @media (min-width: 600px) {
    color: red;
  }
}

Relevant links:

  • Codepen demo
  • https://github.com/w3c/csswg-drafts/issues/9492#issuecomment-2219724141
  • https://github.com/w3c/csswg-drafts/issues/9702
  • https://github.com/w3c/csswg-drafts/issues/2284#issuecomment-363957055

What solution would you like to see?

A new rule that disallows this type of nesting. Autofixing isn't necessary, although probably is possible in simple cases.

bschlenk avatar May 06 '25 05:05 bschlenk

@bschlenk Thanks for the request and for using the template.

It does sound like a good candidate for a rule, and similar to our existing no-invalid and no-unmatchable one.

It's a knotty one that we'll need to define the scope of.

Do we think the rule should catch both :is() pseudo-classes that are:

  • authored by hand, e.g. :is(a::before)
  • desugarred from nesting, e.g. a::before { b & {} }

For the latter, we can likely use the spec-based nesting resolving that we've experimented with in https://github.com/stylelint/stylelint/pull/7496.

Do you know if it's the same limitation for the other logical combination pseudo-classes, like :where() and :not(), and what other psuedo-elements can't be matched, e.g. ::part, ::marker and so on?

jeddy3 avatar May 12 '25 14:05 jeddy3