postcss-pseudo-where-fallback 0.4.2 → 0.5.1

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,23 @@ 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
+
32
+ ### With Object Syntax (Auto-loading)
33
+
34
+ PostCSS can automatically load the plugin by its package name when using object syntax:
35
+
36
+ ```js
37
+ // postcss.config.js
38
+ export default {
39
+ plugins: {
40
+ 'postcss-pseudo-where-fallback': {},
41
+ }
42
+ };
43
+ ```
44
+
45
+ This syntax is commonly used with tools like Vite and automatically resolves the plugin from `node_modules`.
46
+
30
47
  ### With PostCSS CLI
31
48
 
32
49
  ```js
@@ -91,6 +108,43 @@ The plugin keeps the original `:where()` selector for modern browsers (which wil
91
108
  - **Older browsers with `@supports`**: Ignore the invalid `:where()` selector and use the fallback with normal specificity
92
109
  - **Very old browsers** (no `@supports` support): Ignore both the `:where()` and `@supports` blocks, resulting in no styles (these are pre-2013 browsers)
93
110
 
111
+ ## Important Note on Specificity
112
+
113
+ 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:
114
+
115
+ ```css
116
+ /* Your CSS */
117
+ .sidebar :where(.button) {
118
+ background: blue;
119
+ }
120
+
121
+ .button {
122
+ background: red;
123
+ }
124
+ ```
125
+
126
+ **In modern browsers:**
127
+ ```css
128
+ /* .sidebar :where(.button) = 0,1,0 specificity (only .sidebar counts) */
129
+ /* .button = 0,1,0 specificity */
130
+ /* Result: red background (last rule wins due to equal specificity) ✓ */
131
+ ```
132
+
133
+ **In legacy browsers with the fallback:**
134
+ ```css
135
+ .sidebar :where(.button) { background: blue; }
136
+ @supports not selector(:where(*)) {
137
+ .sidebar .button { background: blue; } /* 0,2,0 specificity! */
138
+ }
139
+
140
+ .button { background: red; } /* 0,1,0 specificity */
141
+ /* Result: blue background (fallback wins due to higher specificity) ✗ */
142
+ ```
143
+
144
+ The fallback `.sidebar .button` has **higher specificity** (0,2,0) than the intended override `.button` (0,1,0), causing different behavior in legacy browsers.
145
+
146
+ **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.
147
+
94
148
  ## More Examples
95
149
 
96
150
  ### Selector Lists with Mixed Types
@@ -158,14 +212,6 @@ Output:
158
212
  }
159
213
  ```
160
214
 
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
215
  ## Browser Support
170
216
 
171
217
  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.2",
3
+ "version": "0.5.1",
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"