Skip to content

pseudo elements must be handled like complex selectors #495

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

romainmenke
Copy link
Member

@romainmenke romainmenke commented Jun 21, 2022

TL;DR;

::before:hover {}
/* not equivalent to */
:hover::before {}

More context

a pseudo-element is effectively a new element, the pseudo-element selector is not equivalent in meaning to any other simple selector, and is instead basically a combinator into the "pseudo-children" of an element.

w3c/csswg-drafts#2284 (comment)


This affects every plugin that has any logic around combinators or compound vs complex selectors.

  • any-link
  • cascade layers
  • is-pseudo
  • nesting
  • changelogs

I usually prefer to reduce problems to sorting because sorting is easy and algorithms aren't.

This just backfired because the AST we receive from postcss-selector-parser doesn't consider ::before as <combinator><tag>.

To make things worse I was not even aware that this was an issue to look out for and I was just trying to place pseudo elements at the end of selectors. (breaking ::before:hover)


Cascade layers and any-link were trivial to fix because they only required an exact insertion of new selector bits. I was lazy before and always just added the new bits at the end and then sorted to repair.

The fixes here remove all this "bad" code and implement exact insertion.

You might see :not(#\#) or [href] inserted in a different position than before this change.


is-pseudo (and thus nesting) are much more complex.
We still need the sorting there, but we must consider pseudo elements as combinators.

I've written a new sorting function for is-pseudo and it seems to work.

I avoided making too many changes in nesting as there is no browser implementation yet.
The change here is limited to not re-ordering pseudo elements in a way that changes the selected element.

For is-pseudo I've made the plugin a bit more strict and disallowed pseudo elements in :is(). These are not allowed by the spec. To preserve the forgiving selector list behaviour any pseudo element is replaced by a custom pseudo element which is known to be invalid. ::before -> ::-csstools-invalid-before

@romainmenke romainmenke marked this pull request as ready for review June 22, 2022 20:04
Copy link
Member

@Antonio-Laguna Antonio-Laguna left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, nice and impressive work! So many tests!

…--diligent-american-foxhound-0a232a032c

# Conflicts:
#	plugin-packs/postcss-preset-env/test/layers-basic.expect.css
#	plugin-packs/postcss-preset-env/test/layers-basic.preserve.true.expect.css
#	plugins/postcss-nesting/CHANGELOG.md
#	plugins/postcss-nesting/test/at-rule.no-is-pseudo-selector.expect.css
#	plugins/postcss-nesting/test/basic.expect.css
#	plugins/postcss-nesting/test/basic.no-is-pseudo-selector.expect.css
#	plugins/postcss-nesting/test/ignore.no-is-pseudo-selector.expect.css
@Antonio-Laguna Antonio-Laguna merged commit 6f42261 into main Jun 23, 2022
@Antonio-Laguna Antonio-Laguna deleted the pseudo-elements-are-never-compound-selectors--diligent-american-foxhound-0a232a032c branch June 23, 2022 12:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants