postcss-pseudo-where-fallback 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,6 +27,8 @@ export default {
27
27
  };
28
28
  ```
29
29
 
30
+ **Note:** This plugin currently does not accept any options. Simply use it without arguments: `postcssPluginPseudoWhereFallback()`.
31
+
30
32
  ### With PostCSS CLI
31
33
 
32
34
  ```js
@@ -91,6 +93,43 @@ The plugin keeps the original `:where()` selector for modern browsers (which wil
91
93
  - **Older browsers with `@supports`**: Ignore the invalid `:where()` selector and use the fallback with normal specificity
92
94
  - **Very old browsers** (no `@supports` support): Ignore both the `:where()` and `@supports` blocks, resulting in no styles (these are pre-2013 browsers)
93
95
 
96
+ ## Important Note on Specificity
97
+
98
+ The key feature of `:where()` is that it has **zero specificity**, while the fallback selectors have **normal specificity**. This can result in different behavior in legacy browsers when combined with other selectors:
99
+
100
+ ```css
101
+ /* Your CSS */
102
+ .sidebar :where(.button) {
103
+ background: blue;
104
+ }
105
+
106
+ .button {
107
+ background: red;
108
+ }
109
+ ```
110
+
111
+ **In modern browsers:**
112
+ ```css
113
+ /* .sidebar :where(.button) = 0,1,0 specificity (only .sidebar counts) */
114
+ /* .button = 0,1,0 specificity */
115
+ /* Result: red background (last rule wins due to equal specificity) ✓ */
116
+ ```
117
+
118
+ **In legacy browsers with the fallback:**
119
+ ```css
120
+ .sidebar :where(.button) { background: blue; }
121
+ @supports not selector(:where(*)) {
122
+ .sidebar .button { background: blue; } /* 0,2,0 specificity! */
123
+ }
124
+
125
+ .button { background: red; } /* 0,1,0 specificity */
126
+ /* Result: blue background (fallback wins due to higher specificity) ✗ */
127
+ ```
128
+
129
+ The fallback `.sidebar .button` has **higher specificity** (0,2,0) than the intended override `.button` (0,1,0), causing different behavior in legacy browsers.
130
+
131
+ **Recommendation:** If you're using `:where()` specifically for its zero-specificity behavior in complex cascade scenarios, test thoroughly in legacy browsers or consider using more specific overrides.
132
+
94
133
  ## More Examples
95
134
 
96
135
  ### Selector Lists with Mixed Types
@@ -158,14 +197,6 @@ Output:
158
197
  }
159
198
  ```
160
199
 
161
- ## Options
162
-
163
- This plugin currently does not accept any options. Simply use it without arguments:
164
-
165
- ```js
166
- postcssPluginPseudoWhereFallback()
167
- ```
168
-
169
200
  ## Browser Support
170
201
 
171
202
  This plugin helps support browsers that don't have native `:where()` support, including:
package/dist/index.cjs CHANGED
@@ -1,92 +1,64 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
13
- for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
- // If the importer is in node compatibility mode or this is not an ESM
21
- // file that has been converted to a CommonJS file using a Babel-
22
- // compatible transform (i.e. "__esModule" has not been set), then set
23
- // "default" to the CommonJS "module.exports" for node compatibility.
24
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
- mod
26
- ));
27
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
- var index_exports = {};
29
- __export(index_exports, {
30
- default: () => index_default
31
- });
32
- module.exports = __toCommonJS(index_exports);
33
- var import_postcss = __toESM(require("postcss"), 1);
34
- var import_postcss_selector_parser = __toESM(require("postcss-selector-parser"), 1);
35
- const plugin = () => {
36
- return {
1
+ "use strict";
2
+
3
+ var postcss = require("postcss");
4
+
5
+ var selectorParser = require("postcss-selector-parser");
6
+
7
+ const plugin = () => ({
37
8
  postcssPlugin: "postcss-pseudo-where-fallback",
38
9
  Once(root) {
39
- const rulesToProcess = [];
40
- root.walkRules((rule) => {
41
- if (rule.selector && rule.selector.includes(":where(")) {
42
- rulesToProcess.push(rule);
43
- }
44
- });
45
- rulesToProcess.forEach((rule) => {
46
- const fallbackSelectors = [];
47
- (0, import_postcss_selector_parser.default)((selectors) => {
48
- selectors.each((selector) => {
49
- selector.walkPseudos((pseudo) => {
50
- if (pseudo.value === ":where" && pseudo.nodes) {
51
- const parent = pseudo.parent;
52
- const index = parent.index(pseudo);
53
- const prefix = parent.nodes.slice(0, index);
54
- const suffix = parent.nodes.slice(index + 1);
55
- pseudo.nodes.forEach((whereSelector) => {
56
- let selectorString = "";
57
- prefix.forEach((node) => {
58
- selectorString += node.toString();
59
- });
60
- whereSelector.nodes.forEach((node, i) => {
61
- const nodeStr = node.toString();
62
- if (i === 0) {
63
- selectorString += nodeStr.trimStart();
64
- } else {
65
- selectorString += nodeStr;
66
- }
67
- });
68
- suffix.forEach((node) => {
69
- selectorString += node.toString();
70
- });
71
- fallbackSelectors.push(selectorString);
10
+ const rulesToProcess = [];
11
+ root.walkRules(rule => {
12
+ if (rule.selector && rule.selector.includes(":where(")) {
13
+ rulesToProcess.push(rule);
14
+ }
15
+ });
16
+ rulesToProcess.forEach(rule => {
17
+ const fallbackSelectors = [];
18
+ selectorParser(selectors => {
19
+ selectors.each(selector => {
20
+ selector.walkPseudos(pseudo => {
21
+ if (pseudo.value === ":where" && pseudo.nodes) {
22
+ const parent = pseudo.parent;
23
+ const index = parent.index(pseudo);
24
+ const prefix = parent.nodes.slice(0, index);
25
+ const suffix = parent.nodes.slice(index + 1);
26
+ pseudo.nodes.forEach(whereSelector => {
27
+ let selectorString = "";
28
+ prefix.forEach(node => {
29
+ selectorString += node.toString();
30
+ });
31
+ whereSelector.nodes.forEach((node, i) => {
32
+ const nodeStr = node.toString();
33
+ if (i === 0) {
34
+ selectorString += nodeStr.trimStart();
35
+ } else {
36
+ selectorString += nodeStr;
37
+ }
38
+ });
39
+ suffix.forEach(node => {
40
+ selectorString += node.toString();
41
+ });
42
+ fallbackSelectors.push(selectorString);
43
+ });
44
+ }
45
+ });
72
46
  });
73
- }
47
+ }).processSync(rule.selector);
48
+ const fallbackRule = rule.clone({
49
+ selector: fallbackSelectors.map(s => s.trim()).join(", ")
74
50
  });
75
- });
76
- }).processSync(rule.selector);
77
- const fallbackRule = rule.clone({
78
- selector: fallbackSelectors.map((s) => s.trim()).join(", ")
79
- });
80
- const fallbackSupports = import_postcss.default.atRule({
81
- name: "supports",
82
- params: "not selector(:where(*))",
83
- source: rule.source
51
+ const fallbackSupports = postcss.atRule({
52
+ name: "supports",
53
+ params: "not selector(:where(*))",
54
+ source: rule.source
55
+ });
56
+ fallbackSupports.append(fallbackRule);
57
+ rule.after(fallbackSupports);
84
58
  });
85
- fallbackSupports.append(fallbackRule);
86
- rule.after(fallbackSupports);
87
- });
88
59
  }
89
- };
90
- };
60
+ });
61
+
91
62
  plugin.postcss = true;
92
- var index_default = plugin;
63
+
64
+ module.exports = plugin;
package/dist/index.mjs CHANGED
@@ -1,63 +1,62 @@
1
1
  import postcss from "postcss";
2
+
2
3
  import selectorParser from "postcss-selector-parser";
3
- const plugin = () => {
4
- return {
4
+
5
+ const plugin = () => ({
5
6
  postcssPlugin: "postcss-pseudo-where-fallback",
6
7
  Once(root) {
7
- const rulesToProcess = [];
8
- root.walkRules((rule) => {
9
- if (rule.selector && rule.selector.includes(":where(")) {
10
- rulesToProcess.push(rule);
11
- }
12
- });
13
- rulesToProcess.forEach((rule) => {
14
- const fallbackSelectors = [];
15
- selectorParser((selectors) => {
16
- selectors.each((selector) => {
17
- selector.walkPseudos((pseudo) => {
18
- if (pseudo.value === ":where" && pseudo.nodes) {
19
- const parent = pseudo.parent;
20
- const index = parent.index(pseudo);
21
- const prefix = parent.nodes.slice(0, index);
22
- const suffix = parent.nodes.slice(index + 1);
23
- pseudo.nodes.forEach((whereSelector) => {
24
- let selectorString = "";
25
- prefix.forEach((node) => {
26
- selectorString += node.toString();
27
- });
28
- whereSelector.nodes.forEach((node, i) => {
29
- const nodeStr = node.toString();
30
- if (i === 0) {
31
- selectorString += nodeStr.trimStart();
32
- } else {
33
- selectorString += nodeStr;
34
- }
35
- });
36
- suffix.forEach((node) => {
37
- selectorString += node.toString();
38
- });
39
- fallbackSelectors.push(selectorString);
8
+ const rulesToProcess = [];
9
+ root.walkRules(rule => {
10
+ if (rule.selector && rule.selector.includes(":where(")) {
11
+ rulesToProcess.push(rule);
12
+ }
13
+ });
14
+ rulesToProcess.forEach(rule => {
15
+ const fallbackSelectors = [];
16
+ selectorParser(selectors => {
17
+ selectors.each(selector => {
18
+ selector.walkPseudos(pseudo => {
19
+ if (pseudo.value === ":where" && pseudo.nodes) {
20
+ const parent = pseudo.parent;
21
+ const index = parent.index(pseudo);
22
+ const prefix = parent.nodes.slice(0, index);
23
+ const suffix = parent.nodes.slice(index + 1);
24
+ pseudo.nodes.forEach(whereSelector => {
25
+ let selectorString = "";
26
+ prefix.forEach(node => {
27
+ selectorString += node.toString();
28
+ });
29
+ whereSelector.nodes.forEach((node, i) => {
30
+ const nodeStr = node.toString();
31
+ if (i === 0) {
32
+ selectorString += nodeStr.trimStart();
33
+ } else {
34
+ selectorString += nodeStr;
35
+ }
36
+ });
37
+ suffix.forEach(node => {
38
+ selectorString += node.toString();
39
+ });
40
+ fallbackSelectors.push(selectorString);
41
+ });
42
+ }
43
+ });
40
44
  });
41
- }
45
+ }).processSync(rule.selector);
46
+ const fallbackRule = rule.clone({
47
+ selector: fallbackSelectors.map(s => s.trim()).join(", ")
42
48
  });
43
- });
44
- }).processSync(rule.selector);
45
- const fallbackRule = rule.clone({
46
- selector: fallbackSelectors.map((s) => s.trim()).join(", ")
47
- });
48
- const fallbackSupports = postcss.atRule({
49
- name: "supports",
50
- params: "not selector(:where(*))",
51
- source: rule.source
49
+ const fallbackSupports = postcss.atRule({
50
+ name: "supports",
51
+ params: "not selector(:where(*))",
52
+ source: rule.source
53
+ });
54
+ fallbackSupports.append(fallbackRule);
55
+ rule.after(fallbackSupports);
52
56
  });
53
- fallbackSupports.append(fallbackRule);
54
- rule.after(fallbackSupports);
55
- });
56
57
  }
57
- };
58
- };
58
+ });
59
+
59
60
  plugin.postcss = true;
60
- var index_default = plugin;
61
- export {
62
- index_default as default
63
- };
61
+
62
+ export { plugin as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postcss-pseudo-where-fallback",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "PostCSS plugin to provide fallbacks for :where() pseudo-class",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.mjs",
@@ -17,7 +17,7 @@
17
17
  "LICENSE"
18
18
  ],
19
19
  "scripts": {
20
- "build": "esbuild src/index.mjs --format=esm --outfile=dist/index.mjs && esbuild src/index.mjs --format=cjs --outfile=dist/index.cjs",
20
+ "build": "rollup -c",
21
21
  "test": "node --test",
22
22
  "prepublishOnly": "npm run build"
23
23
  },
@@ -44,9 +44,10 @@
44
44
  },
45
45
  "devDependencies": {
46
46
  "@csstools/postcss-tape": "^7.0.0",
47
- "esbuild": "^0.27.2",
47
+ "@rollup/plugin-terser": "^0.4.4",
48
48
  "postcss": "^8.4.0",
49
- "postcss-selector-parser": "^7.1.1"
49
+ "postcss-selector-parser": "^7.1.1",
50
+ "rollup": "^4.57.1"
50
51
  },
51
52
  "engines": {
52
53
  "node": ">=14.0.0"