diff --git a/package-lock.json b/package-lock.json
index 447527c221..2aa34e0d00 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6940,10 +6940,13 @@
"version": "3.0.4",
"license": "CC0-1.0",
"dependencies": {
- "postcss-selector-parser": "^6.0.10"
+ "@csstools/selector-specificity": "^2.0.1",
+ "postcss-selector-parser": "^6.0.10",
+ "postcss-value-parser": "^4.2.0"
},
- "bin": {
- "css-has-pseudo": "dist/cli.cjs"
+ "devDependencies": {
+ "@mrhenry/core-web": "^0.7.2",
+ "puppeteer": "^13.6.0"
},
"engines": {
"node": "^12 || ^14 || >=16"
@@ -9994,7 +9997,11 @@
"css-has-pseudo": {
"version": "file:plugins/css-has-pseudo",
"requires": {
- "postcss-selector-parser": "^6.0.10"
+ "@csstools/selector-specificity": "^2.0.1",
+ "@mrhenry/core-web": "^0.7.2",
+ "postcss-selector-parser": "^6.0.10",
+ "postcss-value-parser": "^4.2.0",
+ "puppeteer": "^13.6.0"
}
},
"css-prefers-color-scheme": {
diff --git a/plugin-packs/postcss-preset-env/test/basic.autoprefixer.expect.css b/plugin-packs/postcss-preset-env/test/basic.autoprefixer.expect.css
index 83d2f17631..67c580e483 100644
--- a/plugin-packs/postcss-preset-env/test/basic.autoprefixer.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.autoprefixer.expect.css
@@ -269,7 +269,7 @@
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.autoprefixer.false.expect.css b/plugin-packs/postcss-preset-env/test/basic.autoprefixer.false.expect.css
index 83d2f17631..67c580e483 100644
--- a/plugin-packs/postcss-preset-env/test/basic.autoprefixer.false.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.autoprefixer.false.expect.css
@@ -269,7 +269,7 @@
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.ch38.expect.css b/plugin-packs/postcss-preset-env/test/basic.ch38.expect.css
index 42d22f1dd0..8662f17da5 100644
--- a/plugin-packs/postcss-preset-env/test/basic.ch38.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.ch38.expect.css
@@ -189,7 +189,7 @@
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.ch88-ff78-saf10.expect.css b/plugin-packs/postcss-preset-env/test/basic.ch88-ff78-saf10.expect.css
index e2977fde23..aa72d35384 100644
--- a/plugin-packs/postcss-preset-env/test/basic.ch88-ff78-saf10.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.ch88-ff78-saf10.expect.css
@@ -188,7 +188,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.expect.css b/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.expect.css
index 8786d3cea9..13a393a42d 100644
--- a/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.expect.css
@@ -181,7 +181,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.no-is-pseudo.expect.css b/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.no-is-pseudo.expect.css
index 74519e208a..beeba4b2ba 100644
--- a/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.no-is-pseudo.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.ch88-ff78.no-is-pseudo.expect.css
@@ -181,7 +181,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.expect.css b/plugin-packs/postcss-preset-env/test/basic.expect.css
index d0be138469..55763624e3 100644
--- a/plugin-packs/postcss-preset-env/test/basic.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.expect.css
@@ -292,7 +292,7 @@
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.ff49.expect.css b/plugin-packs/postcss-preset-env/test/basic.ff49.expect.css
index 73df8d7c51..34b0a3fa7d 100644
--- a/plugin-packs/postcss-preset-env/test/basic.ff49.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.ff49.expect.css
@@ -185,7 +185,7 @@
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.ff66.expect.css b/plugin-packs/postcss-preset-env/test/basic.ff66.expect.css
index 68ba132440..37806912a9 100644
--- a/plugin-packs/postcss-preset-env/test/basic.ff66.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.ff66.expect.css
@@ -173,7 +173,7 @@
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.ie10.expect.css b/plugin-packs/postcss-preset-env/test/basic.ie10.expect.css
index 8ef43c0138..3c2fbd479e 100644
--- a/plugin-packs/postcss-preset-env/test/basic.ie10.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.ie10.expect.css
@@ -301,7 +301,7 @@
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.nesting.false.expect.css b/plugin-packs/postcss-preset-env/test/basic.nesting.false.expect.css
index d9f1048f96..12dca7cf3a 100644
--- a/plugin-packs/postcss-preset-env/test/basic.nesting.false.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.nesting.false.expect.css
@@ -290,7 +290,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.op_mini.expect.css b/plugin-packs/postcss-preset-env/test/basic.op_mini.expect.css
index ba22fb9369..57e75c0928 100644
--- a/plugin-packs/postcss-preset-env/test/basic.op_mini.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.op_mini.expect.css
@@ -276,7 +276,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.preserve.true.expect.css b/plugin-packs/postcss-preset-env/test/basic.preserve.true.expect.css
index ec6c8885ac..4e01ebd919 100644
--- a/plugin-packs/postcss-preset-env/test/basic.preserve.true.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.preserve.true.expect.css
@@ -524,7 +524,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.safari15.expect.css b/plugin-packs/postcss-preset-env/test/basic.safari15.expect.css
index 73a8f5820f..725600ff58 100644
--- a/plugin-packs/postcss-preset-env/test/basic.safari15.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.safari15.expect.css
@@ -157,7 +157,7 @@
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.stage0-ff49.expect.css b/plugin-packs/postcss-preset-env/test/basic.stage0-ff49.expect.css
index 642c7b7f41..a58b488f01 100644
--- a/plugin-packs/postcss-preset-env/test/basic.stage0-ff49.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.stage0-ff49.expect.css
@@ -190,7 +190,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.stage0-ff66.expect.css b/plugin-packs/postcss-preset-env/test/basic.stage0-ff66.expect.css
index 4e07900568..26f9a3fee5 100644
--- a/plugin-packs/postcss-preset-env/test/basic.stage0-ff66.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.stage0-ff66.expect.css
@@ -178,7 +178,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/basic.stage0.expect.css b/plugin-packs/postcss-preset-env/test/basic.stage0.expect.css
index 3e4de3670c..57e734eae7 100644
--- a/plugin-packs/postcss-preset-env/test/basic.stage0.expect.css
+++ b/plugin-packs/postcss-preset-env/test/basic.stage0.expect.css
@@ -297,7 +297,7 @@ h1.test-custom-selectors,h2.test-custom-selectors,h3.test-custom-selectors,h4.te
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)] {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-1.expect.css b/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-1.expect.css
index b6e6b4162f..ba171663b1 100644
--- a/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-1.expect.css
+++ b/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-1.expect.css
@@ -10,7 +10,7 @@
order: 3;
}
-[\:has\(.a\)] {
+[csstools-has-1m-2w-2p-37-14-1a-2p-15] {
order: 4;
}
diff --git a/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-2.expect.css b/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-2.expect.css
index 92d3a8efde..f72e022552 100644
--- a/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-2.expect.css
+++ b/plugin-packs/postcss-preset-env/test/client-side-polyfills.stage-2.expect.css
@@ -10,7 +10,7 @@
order: 3;
}
-[\:has\(.a\)] {
+[csstools-has-1m-2w-2p-37-14-1a-2p-15] {
order: 4;
}
diff --git a/plugin-packs/postcss-preset-env/test/disable-client-side-polyfills.disabled.expect.css b/plugin-packs/postcss-preset-env/test/disable-client-side-polyfills.disabled.expect.css
index 64a31a360e..1e7ff7369e 100644
--- a/plugin-packs/postcss-preset-env/test/disable-client-side-polyfills.disabled.expect.css
+++ b/plugin-packs/postcss-preset-env/test/disable-client-side-polyfills.disabled.expect.css
@@ -6,7 +6,7 @@ input:blank {
order: 1;
}
-a[\:has\(\%3E\%20img\)] {
+[csstools-has-2p-1m-2w-2p-37-14-1q-w-2x-31-2v-15]:not(does-not-exist):not(does-not-exist) {
order: 2;
}
diff --git a/plugin-packs/postcss-preset-env/test/layers-basic.expect.css b/plugin-packs/postcss-preset-env/test/layers-basic.expect.css
index 1cd7cd9aa8..88a4963f9b 100644
--- a/plugin-packs/postcss-preset-env/test/layers-basic.expect.css
+++ b/plugin-packs/postcss-preset-env/test/layers-basic.expect.css
@@ -474,7 +474,7 @@ h1.test-custom-selectors:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):n
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)]:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#) {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#) {
background-color: yellow;
}
diff --git a/plugin-packs/postcss-preset-env/test/layers-basic.preserve.true.expect.css b/plugin-packs/postcss-preset-env/test/layers-basic.preserve.true.expect.css
index 9b8ae465cd..47a28fae37 100644
--- a/plugin-packs/postcss-preset-env/test/layers-basic.preserve.true.expect.css
+++ b/plugin-packs/postcss-preset-env/test/layers-basic.preserve.true.expect.css
@@ -535,7 +535,7 @@ h1.test-custom-selectors:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):n
background-color: yellow;
}
-.test-has-pseudo-class[\:has\(.inner-class\)]:not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#) {
+[csstools-has-1a-38-2t-37-38-19-2w-2p-37-19-34-37-2t-39-2s-33-19-2r-30-2p-37-37-1m-2w-2p-37-14-1a-2x-32-32-2t-36-19-2r-30-2p-37-37-15]:not(.does-not-exist):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#):not(#\#) {
background-color: yellow;
}
diff --git a/plugins/css-has-pseudo/.gitignore b/plugins/css-has-pseudo/.gitignore
index 3559a9367b..7172b04f10 100644
--- a/plugins/css-has-pseudo/.gitignore
+++ b/plugins/css-has-pseudo/.gitignore
@@ -1,14 +1,6 @@
node_modules
-dist
package-lock.json
yarn.lock
-browser.js
-!src/browser.js
-*.log*
*.result.css
*.result.css.map
-!.editorconfig
-!.gitignore
-!.rollup.js
-!.tape.js
-!.travis.yml
+dist/*
diff --git a/plugins/css-has-pseudo/.tape.mjs b/plugins/css-has-pseudo/.tape.mjs
index 77aab682e8..58629ecdf5 100644
--- a/plugins/css-has-pseudo/.tape.mjs
+++ b/plugins/css-has-pseudo/.tape.mjs
@@ -1,5 +1,8 @@
import postcssTape from '../../packages/postcss-tape/dist/index.mjs';
import plugin from 'css-has-pseudo';
+import postcssLogical from 'postcss-logical';
+import postcssNesting from 'postcss-nesting';
+import postcssDirPseudoClass from 'postcss-dir-pseudo-class';
postcssTape(plugin)({
'basic': {
@@ -11,6 +14,21 @@ postcssTape(plugin)({
preserve: false
}
},
+ 'basic:specificity-matching-name': {
+ message: 'supports { specificityMatchingName: "other-thing-that-does-not-exist" } usage',
+ options: {
+ specificityMatchingName: 'other-thing-that-does-not-exist'
+ }
+ },
+ 'examples/example': {
+ message: 'minimal example',
+ },
+ 'examples/example:preserve-false': {
+ message: 'minimal example',
+ options: {
+ preserve: false
+ }
+ },
'generated-selector-cases': {
message: 'correctly handles generated cases',
warnings: 1,
@@ -18,4 +36,42 @@ postcssTape(plugin)({
preserve: false
}
},
-})
+ 'browser': {
+ message: 'prepare CSS for chrome test',
+ options: {
+ preserve: false
+ }
+ },
+ 'plugin-order-logical:before': {
+ message: 'works with other plugins that modify selectors',
+ plugins: [postcssLogical({ preserve: false }), postcssDirPseudoClass({ preserve: false }), plugin({ preserve: false })],
+ },
+ 'plugin-order-logical:after': {
+ message: 'works with other plugins that modify selectors',
+ plugins: [plugin({ preserve: false }), postcssLogical({ preserve: false }), postcssDirPseudoClass({ preserve: false })],
+ },
+ 'plugin-order-logical:before:preserve': {
+ message: 'works with other plugins that modify selectors',
+ plugins: [postcssLogical({ preserve: true }), postcssDirPseudoClass({ preserve: true }), plugin({ preserve: true })],
+ },
+ 'plugin-order-logical:after:preserve': {
+ message: 'works with other plugins that modify selectors',
+ plugins: [plugin({ preserve: true }), postcssLogical({ preserve: true }), postcssDirPseudoClass({ preserve: true })],
+ },
+ 'plugin-order-nesting:before': {
+ message: 'works with other plugins that modify selectors',
+ plugins: [postcssNesting({ preserve: false }), plugin({ preserve: false })],
+ },
+ 'plugin-order-nesting:after': {
+ message: 'works with other plugins that modify selectors',
+ plugins: [postcssNesting({ preserve: false }), plugin({ preserve: false })],
+ },
+ 'plugin-order-nesting:before:preserve': {
+ message: 'works with other plugins that modify selectors',
+ plugins: [plugin({ preserve: true }), postcssNesting({ preserve: true })],
+ },
+ 'plugin-order-nesting:after:preserve': {
+ message: 'works with other plugins that modify selectors',
+ plugins: [plugin({ preserve: true }), postcssNesting({ preserve: true })],
+ }
+});
diff --git a/plugins/css-has-pseudo/CHANGELOG.md b/plugins/css-has-pseudo/CHANGELOG.md
index 5cbec5ed43..141631b3b7 100644
--- a/plugins/css-has-pseudo/CHANGELOG.md
+++ b/plugins/css-has-pseudo/CHANGELOG.md
@@ -1,5 +1,32 @@
# Changes to CSS Has Pseudo
+### Unreleased (major)
+
+[Read the full changelog](https://github.com/csstools/postcss-plugins/wiki/PostCSS-Preset-Env-8)
+
+- Breaking: removed old CDN urls
+- Added: 'hover' options for browser polyfill
+- Added: 'observedAttributes' options for browser polyfill
+- Added: 'forcePolyfill' options for browser polyfill
+- Added: Rules within `@supports selector(:has(something))` won't be transformed.
+- Fix: Use base36 encoding to support all possible selectors.
+
+#### How to migrate :
+
+##### Re-build your CSS with the new version of the library.
+
+##### If you use a CDN url, please update it.
+
+```diff
+-
++
+```
+
+```diff
+-
++
+```
+
### 3.0.4 (February 5, 2022)
- Rebuild of browser polyfills
diff --git a/plugins/css-has-pseudo/INSTALL.md b/plugins/css-has-pseudo/INSTALL.md
index 49780082c5..67915df268 100644
--- a/plugins/css-has-pseudo/INSTALL.md
+++ b/plugins/css-has-pseudo/INSTALL.md
@@ -1,13 +1,13 @@
-# Installing CSS Has Pseudo
+# Installing PostCSS Has Pseudo
-[CSS Has Pseudo] runs in all Node environments, with special instructions for:
+[PostCSS Has Pseudo] runs in all Node environments, with special instructions for:
| [Node](#node) | [PostCSS CLI](#postcss-cli) | [Webpack](#webpack) | [Create React App](#create-react-app) | [Gulp](#gulp) | [Grunt](#grunt) |
| --- | --- | --- | --- | --- | --- |
## Node
-Add [CSS Has Pseudo] to your project:
+Add [PostCSS Has Pseudo] to your project:
```bash
npm install postcss css-has-pseudo --save-dev
@@ -32,7 +32,7 @@ Add [PostCSS CLI] to your project:
npm install postcss-cli css-has-pseudo --save-dev
```
-Use [CSS Has Pseudo] in your `postcss.config.js` configuration file:
+Use [PostCSS Has Pseudo] in your `postcss.config.js` configuration file:
```js
const postcssHasPseudo = require('css-has-pseudo');
@@ -54,7 +54,7 @@ Add [PostCSS Loader] to your project:
npm install postcss-loader css-has-pseudo --save-dev
```
-Use [CSS Has Pseudo] in your Webpack configuration:
+Use [PostCSS Has Pseudo] in your Webpack configuration:
```js
module.exports = {
@@ -98,7 +98,7 @@ Add [React App Rewired] and [React App Rewire PostCSS] to your project:
npm install react-app-rewired react-app-rewire-postcss css-has-pseudo --save-dev
```
-Use [React App Rewire PostCSS] and [CSS Has Pseudo] in your
+Use [React App Rewire PostCSS] and [PostCSS Has Pseudo] in your
`config-overrides.js` file:
```js
@@ -120,7 +120,7 @@ Add [Gulp PostCSS] to your project:
npm install gulp-postcss css-has-pseudo --save-dev
```
-Use [CSS Has Pseudo] in your Gulpfile:
+Use [PostCSS Has Pseudo] in your Gulpfile:
```js
const postcss = require('gulp-postcss');
@@ -145,7 +145,7 @@ Add [Grunt PostCSS] to your project:
npm install grunt-postcss css-has-pseudo --save-dev
```
-Use [CSS Has Pseudo] in your Gruntfile:
+Use [PostCSS Has Pseudo] in your Gruntfile:
```js
const postcssHasPseudo = require('css-has-pseudo');
@@ -171,6 +171,6 @@ grunt.initConfig({
[PostCSS]: https://github.com/postcss/postcss
[PostCSS CLI]: https://github.com/postcss/postcss-cli
[PostCSS Loader]: https://github.com/postcss/postcss-loader
-[CSS Has Pseudo]: https://github.com/csstools/postcss-plugins/tree/main/plugins/css-has-pseudo
+[PostCSS Has Pseudo]: https://github.com/csstools/postcss-plugins/tree/main/plugins/css-has-pseudo
[React App Rewire PostCSS]: https://github.com/csstools/react-app-rewire-postcss
[React App Rewired]: https://github.com/timarney/react-app-rewired
diff --git a/plugins/css-has-pseudo/README-BROWSER.md b/plugins/css-has-pseudo/README-BROWSER.md
deleted file mode 100644
index 3af212ef19..0000000000
--- a/plugins/css-has-pseudo/README-BROWSER.md
+++ /dev/null
@@ -1,67 +0,0 @@
-# CSS Has Pseudo for Browsers [][CSS Has Pseudo]
-
-[![NPM Version][npm-img]][npm-url]
-[][discord]
-
-[CSS Has Pseudo] lets you style elements relative to other elements in CSS,
-following the [Selectors Level 4] specification.
-
-```css
-input {
- /* style an input */
-}
-
-body[\:has\(\:focus\)] {
- /* style an input without a value */
-}
-```
-
-## Usage
-
-Add [CSS Has Pseudo] to your build tool:
-
-```bash
-npm install css-has-pseudo
-```
-
-Then include and initialize it on your document:
-
-```js
-const cssHasPseudo = require('css-has-pseudo/browser');
-
-cssHasPseudo(document);
-```
-
-```html
-
-
-
-```
-
-## Dependencies
-
-Web API's:
-
-- [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
-- [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)
-- [querySelectorAll](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) with support for post CSS 2.1 selectors
-
-ECMA Script:
-
-- `Array.prototype.filter`
-- `Array.prototype.forEach`
-- `Array.prototype.indexOf`
-- `Array.prototype.join`
-- `Array.prototype.map`
-- `Array.prototype.splice`
-- `RegExp.prototype.exec`
-- `String.prototype.match`
-- `String.prototype.replace`
-- `String.prototype.split`
-
-[discord]: https://discord.gg/bUadyRwkJS
-[npm-img]: https://img.shields.io/npm/v/css-has-pseudo.svg
-[npm-url]: https://www.npmjs.com/package/css-has-pseudo
-
-[CSS Has Pseudo]: https://github.com/csstools/postcss-plugins/tree/main/plugins/css-has-pseudo
-[Selectors Level 4]: https://drafts.csswg.org/selectors-4/#has
diff --git a/plugins/css-has-pseudo/README-POSTCSS.md b/plugins/css-has-pseudo/README-POSTCSS.md
deleted file mode 100644
index 6f50c2dc5e..0000000000
--- a/plugins/css-has-pseudo/README-POSTCSS.md
+++ /dev/null
@@ -1,91 +0,0 @@
-# CSS Has Pseudo for PostCSS [][CSS Has Pseudo]
-
-[![NPM Version][npm-img]][npm-url]
-[][discord]
-
-[CSS Has Pseudo] lets you style elements relative to other elements in CSS,
-following the [Selectors Level 4] specification.
-
-```css
-body:has(:focus) {
- background-color: yellow;
-}
-
-/* becomes */
-
-body[\:has\(\:focus\)] {
- background-color: yellow;
-}
-
-body:has(:focus) {
- background-color: yellow;
-}
-```
-
-[CSS Has Pseudo] duplicates rules using the `:has` pseudo-class with a `[has]`
-attribute selector. The preservation of the original `:has` rule can be
-disabled using the `preserve` option.
-
-## Usage
-
-Add [CSS Has Pseudo] to your project:
-
-```bash
-npm install css-has-pseudo --save-dev
-```
-
-Use [CSS Has Pseudo] to process your CSS:
-
-```js
-const postcssHasPseudo = require('css-has-pseudo');
-
-postcssHasPseudo.process(YOUR_CSS /*, processOptions, pluginOptions */);
-```
-
-Or use it as a [PostCSS] plugin:
-
-```js
-const postcss = require('postcss');
-const postcssHasPseudo = require('css-has-pseudo');
-
-postcss([
- postcssHasPseudo(/* pluginOptions */)
-]).process(YOUR_CSS /*, processOptions */);
-```
-
-[CSS Has Pseudo] runs in all Node environments, with special
-instructions for:
-
-| [Node](INSTALL.md#node) | [PostCSS CLI](INSTALL.md#postcss-cli) | [Webpack](INSTALL.md#webpack) | [Create React App](INSTALL.md#create-react-app) | [Gulp](INSTALL.md#gulp) | [Grunt](INSTALL.md#grunt) |
-| --- | --- | --- | --- | --- | --- |
-
-## Options
-
-### preserve
-
-The `preserve` option defines whether the original selector should remain. By
-default, the original selector is preserved.
-
-```js
-hasPseudo({ preserve: false });
-```
-
-```css
-body:has(:focus) {
- background-color: yellow;
-}
-
-/* becomes */
-
-body[\:has\(\:focus\)] {
- background-color: yellow;
-}
-```
-
-[discord]: https://discord.gg/bUadyRwkJS
-[npm-img]: https://img.shields.io/npm/v/css-has-pseudo.svg
-[npm-url]: https://www.npmjs.com/package/css-has-pseudo
-
-[PostCSS]: https://github.com/postcss/postcss
-[CSS Has Pseudo]: https://github.com/csstools/postcss-plugins/tree/main/plugins/css-has-pseudo
-[Selectors Level 4]: https://drafts.csswg.org/selectors-4/#has-pseudo
diff --git a/plugins/css-has-pseudo/README.md b/plugins/css-has-pseudo/README.md
index 0125e3fd5c..d19fe1266e 100644
--- a/plugins/css-has-pseudo/README.md
+++ b/plugins/css-has-pseudo/README.md
@@ -1,105 +1,308 @@
-# CSS Has Pseudo [][CSS Has Pseudo]
+# PostCSS Has Pseudo [][postcss]
-[![NPM Version][npm-img]][npm-url]
-[][discord]
+[][npm-url] [][css-url] [][cli-url] [][discord]
-[CSS Has Pseudo] lets you style elements relative to other elements in CSS,
-following the [Selectors Level 4] specification.
+[PostCSS Has Pseudo] lets you style elements relative to other elements in CSS, following the [Selectors Level 4] specification.
-[](https://caniuse.com/#feat=css-has)
-
-```css
-a:has(> img) {
- /* style links that contain an image */
+```pcss
+.title:has(+ p) {
+ margin-bottom: 1.5rem;
}
-h1:has(+ p) {
- /* style level 1 headings that are followed by a paragraph */
-}
+/* becomes */
-section:not(:has(h1, h2, h3, h4, h5, h6)) {
- /* style sections that don’t contain any heading elements */
+[csstools-has-1a-38-2x-38-30-2t-1m-2w-2p-37-14-17-w-34-15]:not(does-not-exist) {
+ margin-bottom: 1.5rem;
}
-
-body:has(:focus) {
- /* style the body if it contains a focused element */
+.title:has(+ p) {
+ margin-bottom: 1.5rem;
}
```
## Usage
-From the command line, transform CSS files that use `:has` selectors:
+Add [PostCSS Has Pseudo] to your project:
```bash
-npx css-has-pseudo SOURCE.css --output TRANSFORMED.css
+npm install postcss css-has-pseudo --save-dev
```
-Next, use your transformed CSS with this script:
+Use it as a [PostCSS] plugin:
-```html
-
-
-
+```js
+const postcss = require('postcss');
+const postcssHasPseudo = require('css-has-pseudo');
+
+postcss([
+ postcssHasPseudo(/* pluginOptions */)
+]).process(YOUR_CSS /*, processOptions */);
```
-⚠️ Please use a versioned url, like this : `https://unpkg.com/css-has-pseudo@3.0.0/dist/browser-global.js`
-Without the version, you might unexpectedly get a new major version of the library with breaking changes.
+[PostCSS Has Pseudo] runs in all Node environments, with special
+instructions for:
-⚠️ If you were using an older version via a CDN, please update the entire url.
-The old URL will no longer work in a future release.
+| [Node](INSTALL.md#node) | [PostCSS CLI](INSTALL.md#postcss-cli) | [Webpack](INSTALL.md#webpack) | [Create React App](INSTALL.md#create-react-app) | [Gulp](INSTALL.md#gulp) | [Grunt](INSTALL.md#grunt) |
+| --- | --- | --- | --- | --- | --- |
-That’s it. The script is 765 bytes and works in most browser versions, including
-Internet Explorer 11. With a [Mutation Observer polyfill], the script will work
-down to Internet Explorer 9.
+## Options
-See [README BROWSER](README-BROWSER.md) for more information.
+### preserve
-## How it works
+The `preserve` option determines whether the original notation
+is preserved. By default the original rules are preserved.
-The [PostCSS plugin](README-POSTCSS.md) clones rules containing `:has`,
-replacing them with an alternative `[:has]` selector.
+```js
+postcssHasPseudo({ preserve: false })
+```
-```css
-body:has(:focus) {
- background-color: yellow;
+```pcss
+.title:has(+ p) {
+ margin-bottom: 1.5rem;
}
-section:not(:has(h1, h2, h3, h4, h5, h6)) {
- background-color: gray;
+/* becomes */
+
+[csstools-has-1a-38-2x-38-30-2t-1m-2w-2p-37-14-17-w-34-15]:not(does-not-exist) {
+ margin-bottom: 1.5rem;
}
+```
-/* becomes */
+### specificityMatchingName
+
+The `specificityMatchingName` option allows you to change to selector that is used to adjust specificity.
+The default value is `does-not-exist`.
+If this is an actual class, id or tag name in your code, you will need to set a different option here.
+
+See how `:not` is used to modify [specificity](#specificity).
+
+```js
+postcssHasPseudo({ specificityMatchingName: 'something-random' })
+```
-body[\:has\(\:focus\)] {
- background-color: yellow;
+[specificity 1, 2, 0](https://polypane.app/css-specificity-calculator/#selector=.x%3Ahas(%3E%20%23a%3Ahover))
+
+Before :
+
+```css
+.x:has(> #a:hover) {
+ order: 11;
}
+```
-body:has(:focus) {
- background-color: yellow;
+After :
+
+[specificity 1, 2, 0](https://polypane.app/css-specificity-calculator/#selector=%5Bcsstools-has-1a-3c-1m-2w-2p-37-14-1q-w-z-2p-1m-2w-33-3a-2t-36-15%5D%3Anot(%23does-not-exist)%3Anot(.does-not-exist))
+
+```css
+[csstools-has-1a-3c-1m-2w-2p-37-14-1q-w-z-2p-1m-2w-33-3a-2t-36-15]:not(#does-not-exist):not(.does-not-exist) {
+ order: 11;
}
+```
+
+## ⚠️ Known shortcomings
+
+### Specificity
+
+`:has` transforms will result in at least one attribute selector with specificity `0, 1, 0`.
+If your selector only has tags we won't be able to match the original specificity.
+
+Before :
+
+[specificity 0, 0, 2](https://polypane.app/css-specificity-calculator/#selector=figure%3Ahas(%3E%20img))
+
+```css
+figure:has(> img)
+```
+
+After :
+
+[specificity 0, 1, 2](https://polypane.app/css-specificity-calculator/#selector=%5Bcsstools-has-2u-2x-2v-39-36-2t-1m-2w-2p-37-14-1q-w-2x-31-2v-15%5D%3Anot(does-not-exist)%3Anot(does-not-exist))
+
+```css
+[csstools-has-2u-2x-2v-39-36-2t-1m-2w-2p-37-14-1q-w-2x-31-2v-15]:not(does-not-exist):not(does-not-exist)
+```
+
+### Plugin order
+
+As selectors are encoded, this plugin (or `postcss-preset-env`) must be run after any other plugin that transforms selectors.
+
+If other plugins are used, you need to place these in your config before `postcss-preset-env` or `css-has-pseudo`.
+
+Please let us know if you have issues with plugins that transform selectors.
+Then we can investigate and maybe fix these.
+
+## Browser
+
+```js
+// initialize prefersColorScheme (applies the current OS color scheme, if available)
+import cssHasPseudo from 'css-has-pseudo/browser';
+cssHasPseudo(document);
+```
+
+or
+
+```html
+
+
+
+```
+
+⚠️ Please use a versioned url, like this : `https://unpkg.com/css-has-pseudo@3.0.4/dist/browser-global.js`
+Without the version, you might unexpectedly get a new major version of the library with breaking changes.
-section[\:not-has\(h1\,\%20h2\,\%20h3\,\%20h4\,\%20h5\,\%20h6\)] {
- background-color: gray;
+[PostCSS Has Pseudo] works in all major browsers, including
+Internet Explorer 11. With a [Mutation Observer polyfill](https://github.com/webmodules/mutation-observer), the script will work
+down to Internet Explorer 9.
+
+### Browser Usage
+
+#### hover
+
+The `hover` option determines if `:hover` pseudo-class should be tracked.
+This is disabled by default because it is an expensive operation.
+
+```js
+cssHasPseudo(document, { hover: true });
+```
+
+#### observedAttributes
+
+The `observedAttributes` option determines which html attributes are observed.
+If you do any client side modification of non-standard attributes and use these in combination with `:has()` you should add these here.
+
+```js
+cssHasPseudo(document, { observedAttributes: ['something-not-standard'] });
+```
+
+#### forcePolyfill
+
+The `forcePolyfill` option determines if the polyfill is used even when the browser has native support.
+This is needed when you set `preserve: false` in the PostCSS plugin config.
+
+```js
+cssHasPseudo(document, { forcePolyfill: true });
+```
+
+#### debug
+
+The `debug` option determines if errors are emitted to the console in browser.
+By default the polyfill will not emit errors or warnings.
+
+```js
+cssHasPseudo(document, { debug: true });
+```
+
+
+### Browser Dependencies
+
+Web API's:
+
+- [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
+- [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)
+- [querySelectorAll](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) with support for post CSS 2.1 selectors and `:scope` selectors.
+
+ECMA Script:
+
+- `Array.prototype.filter`
+- `Array.prototype.forEach`
+- `Array.prototype.indexOf`
+- `Array.prototype.join`
+- `Array.prototype.map`
+- `Array.prototype.splice`
+- `RegExp.prototype.exec`
+- `String.prototype.match`
+- `String.prototype.replace`
+- `String.prototype.split`
+
+## CORS
+
+⚠️ Applies to you if you load CSS from a different domain than the page.
+
+In this case the CSS is treated as untrusted and will not be made available to the JavaScript polyfill.
+The polyfill will not work without applying the correct configuration for CORS.
+
+Example :
+
+| page | css | CORS applies |
+| --- | --- | --- |
+| https://example.com/ | https://example.com/style.css | no |
+| https://example.com/ | https://other.com/style.css | yes |
+
+
+**You might see one of these error messages :**
+
+Chrome :
+
+> DOMException: Failed to read the 'cssRules' property from 'CSSStyleSheet': Cannot access rules
+
+Safari :
+
+> SecurityError: Not allowed to access cross-origin stylesheet
+
+Firefox :
+
+> DOMException: CSSStyleSheet.cssRules getter: Not allowed to access cross-origin stylesheet
+
+To resolve CORS errors you need to take two steps :
+
+- add an HTTP header `Access-Control-Allow-Origin: ` when serving your CSS file.
+- add `crossorigin="anonymous"` to the `` tag for your CSS file.
+
+In a node server setting the HTTP header might look like this :
+
+```js
+// http://localhost:8080 is the domain of your page!
+res.setHeader('Access-Control-Allow-Origin', 'https://example.com');
+```
+
+You can also configure a wildcard but please be aware that this might be a security risk.
+It is better to only set the header for the domain you want to allow and only on the responses you want to allow.
+
+HTML might look like this :
+
+```html
+
+```
+
+
+## How it works
+
+The [PostCSS Has Pseudo] clones rules containing `:has()`,
+replacing them with an alternative `[csstools-has-]` selector.
+
+```pcss
+.title:has(+ p) {
+ margin-bottom: 1.5rem;
}
-section:not(:has(h1, h2, h3, h4, h5, h6)) {
- background-color: gray;
+/* becomes */
+
+[csstools-has-1a-38-2x-38-30-2t-1m-2w-2p-37-14-17-w-34-15]:not(does-not-exist) {
+ margin-bottom: 1.5rem;
+}
+.title:has(+ p) {
+ margin-bottom: 1.5rem;
}
```
-Next, the [JavaScript library](README-BROWSER.md) adds a `[:has]` attribute to
+Next, the [browser script](#browser) adds a `[:has]` attribute to
elements otherwise matching `:has` natively.
```html
-
-
-
+
+
A title block
+
With an extra paragraph
+
```
+[cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test
+[css-url]: https://cssdb.org/#has-pseudo-class
[discord]: https://discord.gg/bUadyRwkJS
-[npm-img]: https://img.shields.io/npm/v/css-has-pseudo.svg
[npm-url]: https://www.npmjs.com/package/css-has-pseudo
-[CSS Has Pseudo]: https://github.com/csstools/postcss-plugins/tree/main/plugins/css-has-pseudo
-[Mutation Observer polyfill]: https://github.com/webmodules/mutation-observer
-[Selectors Level 4]: https://drafts.csswg.org/selectors-4/#has-pseudo
+[Gulp PostCSS]: https://github.com/postcss/gulp-postcss
+[Grunt PostCSS]: https://github.com/nDmitry/grunt-postcss
+[PostCSS]: https://github.com/postcss/postcss
+[PostCSS Loader]: https://github.com/postcss/postcss-loader
+[PostCSS Has Pseudo]: https://github.com/csstools/postcss-plugins/tree/main/plugins/css-has-pseudo
+[Selectors Level 4]: https://www.w3.org/TR/selectors-4/#has-pseudo
diff --git a/plugins/css-has-pseudo/docs/README.md b/plugins/css-has-pseudo/docs/README.md
new file mode 100644
index 0000000000..66592a64de
--- /dev/null
+++ b/plugins/css-has-pseudo/docs/README.md
@@ -0,0 +1,225 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+[] lets you style elements relative to other elements in CSS, following the [Selectors Level 4] specification.
+
+```pcss
+
+
+/* becomes */
+
+
+```
+
+
+
+
+
+## Options
+
+### preserve
+
+The `preserve` option determines whether the original notation
+is preserved. By default the original rules are preserved.
+
+```js
+({ preserve: false })
+```
+
+```pcss
+
+
+/* becomes */
+
+
+```
+
+### specificityMatchingName
+
+The `specificityMatchingName` option allows you to change to selector that is used to adjust specificity.
+The default value is `does-not-exist`.
+If this is an actual class, id or tag name in your code, you will need to set a different option here.
+
+See how `:not` is used to modify [specificity](#specificity).
+
+```js
+({ specificityMatchingName: 'something-random' })
+```
+
+[specificity 1, 2, 0](https://polypane.app/css-specificity-calculator/#selector=.x%3Ahas(%3E%20%23a%3Ahover))
+
+Before :
+
+```css
+.x:has(> #a:hover) {
+ order: 11;
+}
+```
+
+After :
+
+[specificity 1, 2, 0](https://polypane.app/css-specificity-calculator/#selector=%5Bcsstools-has-1a-3c-1m-2w-2p-37-14-1q-w-z-2p-1m-2w-33-3a-2t-36-15%5D%3Anot(%23does-not-exist)%3Anot(.does-not-exist))
+
+```css
+[csstools-has-1a-3c-1m-2w-2p-37-14-1q-w-z-2p-1m-2w-33-3a-2t-36-15]:not(#does-not-exist):not(.does-not-exist) {
+ order: 11;
+}
+```
+
+## ⚠️ Known shortcomings
+
+### Specificity
+
+`:has` transforms will result in at least one attribute selector with specificity `0, 1, 0`.
+If your selector only has tags we won't be able to match the original specificity.
+
+Before :
+
+[specificity 0, 0, 2](https://polypane.app/css-specificity-calculator/#selector=figure%3Ahas(%3E%20img))
+
+```css
+figure:has(> img)
+```
+
+After :
+
+[specificity 0, 1, 2](https://polypane.app/css-specificity-calculator/#selector=%5Bcsstools-has-2u-2x-2v-39-36-2t-1m-2w-2p-37-14-1q-w-2x-31-2v-15%5D%3Anot(does-not-exist)%3Anot(does-not-exist))
+
+```css
+[csstools-has-2u-2x-2v-39-36-2t-1m-2w-2p-37-14-1q-w-2x-31-2v-15]:not(does-not-exist):not(does-not-exist)
+```
+
+### Plugin order
+
+As selectors are encoded, this plugin (or `postcss-preset-env`) must be run after any other plugin that transforms selectors.
+
+If other plugins are used, you need to place these in your config before `postcss-preset-env` or `css-has-pseudo`.
+
+Please let us know if you have issues with plugins that transform selectors.
+Then we can investigate and maybe fix these.
+
+## Browser
+
+```js
+// initialize prefersColorScheme (applies the current OS color scheme, if available)
+import cssHasPseudo from '/browser';
+cssHasPseudo(document);
+```
+
+or
+
+```html
+
+
+
+```
+
+⚠️ Please use a versioned url, like this : `https://unpkg.com/@/dist/browser-global.js`
+Without the version, you might unexpectedly get a new major version of the library with breaking changes.
+
+[] works in all major browsers, including
+Internet Explorer 11. With a [Mutation Observer polyfill](https://github.com/webmodules/mutation-observer), the script will work
+down to Internet Explorer 9.
+
+### Browser Usage
+
+#### hover
+
+The `hover` option determines if `:hover` pseudo-class should be tracked.
+This is disabled by default because it is an expensive operation.
+
+```js
+cssHasPseudo(document, { hover: true });
+```
+
+#### observedAttributes
+
+The `observedAttributes` option determines which html attributes are observed.
+If you do any client side modification of non-standard attributes and use these in combination with `:has()` you should add these here.
+
+```js
+cssHasPseudo(document, { observedAttributes: ['something-not-standard'] });
+```
+
+#### forcePolyfill
+
+The `forcePolyfill` option determines if the polyfill is used even when the browser has native support.
+This is needed when you set `preserve: false` in the PostCSS plugin config.
+
+```js
+cssHasPseudo(document, { forcePolyfill: true });
+```
+
+#### debug
+
+The `debug` option determines if errors are emitted to the console in browser.
+By default the polyfill will not emit errors or warnings.
+
+```js
+cssHasPseudo(document, { debug: true });
+```
+
+
+### Browser Dependencies
+
+Web API's:
+
+- [MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
+- [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)
+- [querySelectorAll](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) with support for post CSS 2.1 selectors and `:scope` selectors.
+
+ECMA Script:
+
+- `Array.prototype.filter`
+- `Array.prototype.forEach`
+- `Array.prototype.indexOf`
+- `Array.prototype.join`
+- `Array.prototype.map`
+- `Array.prototype.splice`
+- `RegExp.prototype.exec`
+- `String.prototype.match`
+- `String.prototype.replace`
+- `String.prototype.split`
+
+
+
+## How it works
+
+The [] clones rules containing `:has()`,
+replacing them with an alternative `[csstools-has-]` selector.
+
+```pcss
+
+
+/* becomes */
+
+
+```
+
+Next, the [browser script](#browser) adds a `[:has]` attribute to
+elements otherwise matching `:has` natively.
+
+```html
+