purgecss icon indicating copy to clipboard operation
purgecss copied to clipboard

[Bug]: Most pseudo-classes incorrectly purged when there is no selector before it

Open xNyfPtx opened this issue 1 year ago • 5 comments

Describe the bug

It removes psedo-classes without a selector before it.

/* This will be removed and purged incorrectly */

:hover { }
:not(:hover) { }
:where() {}
:where(:hover) {}


etc..

/* This will not be removed and purged incorrectly */

*:hover { }
.class:hover { }
#id:hover { }
[attribute]:hover { }
::after { }
::before { }

and etc.

It doesn't affect pseudo-elements like ::after but affects pseudo-classes. There are some exceptions which are :root but I haven't found more exceptions beyond that

The bug can be sourced from https://purgecss.com/extractors.html

Does not consider special characters such as @, :, /

To Reproduce

Already described

Expected Behavior

I was expecting them to be not removed in the CSS

Environment

OS: Linux Mint; Package: purgecss; Version: 6.0.0;

Add any other context about the problem here

No response

Code of Conduct

  • [x] I agree to follow this project's Code of Conduct

xNyfPtx avatar Oct 04 '24 07:10 xNyfPtx

Just noticed that in the Astro integration. It is particularly problematic with things like:

:where(h1, h2, h3) {
  ...
}

minimaldesign avatar Oct 04 '24 20:10 minimaldesign

I use a CSS library called Open Props and it uses :where(html) to contain the variables to avoid any specificty conflict and I noticed that it affected other pseudo-selectors aswell

xNyfPtx avatar Oct 05 '24 00:10 xNyfPtx

Just noticed that in the Astro integration. It is particularly problematic with things like:

:where(h1, h2, h3) {
  ...
}

You can circumvent this by adding the * or global selector before it and it will be purged correctly. The only issue with this is that it may decrease performance

xNyfPtx avatar Oct 05 '24 05:10 xNyfPtx

As of 👆, it seems some of the pseudo-class issues are fixed but not entirely. Notably, the combination of :is and :not don't seem to work as expected. That is given this:

is(button, input):not(.some-class) { ... }

The rule will get purged even if you have a button or input without .some-class in your HTML.

Repro at https://github.com/fongandrew/purgecss-is-not-bug-repro.

fongandrew avatar May 16 '25 18:05 fongandrew

+1 This breaks optimization of the default Gutenberg styles in WordPress because they use these selectors a lot. E.g.,

.is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)){
  max-width: var(--wp--style--global--content-size);
  margin-left: auto !important;
  margin-right: auto !important;
}

gets removed on my pages with the .alignwide layout despite the fact that they match the selector.

Similarly, I also lose this selector despite the fact that .wp-site-blocks has children.

:where(.wp-site-blocks) > :first-child {
  margin-block-start: 0;
}

mwt avatar May 28 '25 01:05 mwt