tailwindcss 0.0.0-insiders.fb796cd → 0.0.0-insiders.fbbba6f
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/lib/corePlugins.js +38 -5
- package/lib/lib/expandApplyAtRules.js +5 -0
- package/lib/lib/load-config.js +9 -5
- package/lib/lib/sharedState.js +6 -1
- package/lib/util/applyImportantSelector.js +24 -12
- package/lib/util/formatVariantSelector.js +2 -88
- package/lib/util/normalizeConfig.js +0 -14
- package/lib/util/pseudoElements.js +223 -0
- package/lib/util/validateConfig.js +11 -0
- package/package.json +1 -1
- package/src/corePlugins.js +57 -5
- package/src/lib/expandApplyAtRules.js +6 -0
- package/src/lib/load-config.ts +9 -5
- package/src/lib/sharedState.js +15 -6
- package/src/util/applyImportantSelector.js +20 -12
- package/src/util/formatVariantSelector.js +2 -98
- package/src/util/normalizeConfig.js +0 -17
- package/src/util/pseudoElements.js +170 -0
- package/src/util/validateConfig.js +13 -0
- package/stubs/config.full.js +23 -0
- package/types/config.d.ts +1 -6
- package/types/generated/default-theme.d.ts +24 -0
package/lib/corePlugins.js
CHANGED
|
@@ -2740,30 +2740,63 @@ let corePlugins = {
|
|
|
2740
2740
|
"any"
|
|
2741
2741
|
]
|
|
2742
2742
|
};
|
|
2743
|
+
let positionOptions = {
|
|
2744
|
+
values: theme("gradientColorStopPositions"),
|
|
2745
|
+
type: [
|
|
2746
|
+
"length",
|
|
2747
|
+
"percentage"
|
|
2748
|
+
]
|
|
2749
|
+
};
|
|
2743
2750
|
matchUtilities({
|
|
2744
2751
|
from: (value)=>{
|
|
2745
2752
|
let transparentToValue = transparentTo(value);
|
|
2746
2753
|
return {
|
|
2747
|
-
"--tw-gradient-from": (0, _toColorValue.default)(value, "from")
|
|
2748
|
-
"--tw-gradient-
|
|
2754
|
+
"--tw-gradient-from": `${(0, _toColorValue.default)(value, "from")} var(--tw-gradient-from-position)`,
|
|
2755
|
+
"--tw-gradient-from-position": " ",
|
|
2756
|
+
"--tw-gradient-to": `${transparentToValue} var(--tw-gradient-from-position)`,
|
|
2757
|
+
"--tw-gradient-to-position": " ",
|
|
2749
2758
|
"--tw-gradient-stops": `var(--tw-gradient-from), var(--tw-gradient-to)`
|
|
2750
2759
|
};
|
|
2751
2760
|
}
|
|
2752
2761
|
}, options);
|
|
2762
|
+
matchUtilities({
|
|
2763
|
+
from: (value)=>{
|
|
2764
|
+
return {
|
|
2765
|
+
"--tw-gradient-from-position": value
|
|
2766
|
+
};
|
|
2767
|
+
}
|
|
2768
|
+
}, positionOptions);
|
|
2753
2769
|
matchUtilities({
|
|
2754
2770
|
via: (value)=>{
|
|
2755
2771
|
let transparentToValue = transparentTo(value);
|
|
2756
2772
|
return {
|
|
2757
|
-
"--tw-gradient-
|
|
2758
|
-
"--tw-gradient-
|
|
2773
|
+
"--tw-gradient-via-position": " ",
|
|
2774
|
+
"--tw-gradient-to": `${transparentToValue} var(--tw-gradient-to-position)`,
|
|
2775
|
+
"--tw-gradient-to-position": " ",
|
|
2776
|
+
"--tw-gradient-stops": `var(--tw-gradient-from), ${(0, _toColorValue.default)(value, "via")} var(--tw-gradient-via-position), var(--tw-gradient-to)`
|
|
2759
2777
|
};
|
|
2760
2778
|
}
|
|
2761
2779
|
}, options);
|
|
2780
|
+
matchUtilities({
|
|
2781
|
+
via: (value)=>{
|
|
2782
|
+
return {
|
|
2783
|
+
"--tw-gradient-via-position": value
|
|
2784
|
+
};
|
|
2785
|
+
}
|
|
2786
|
+
}, positionOptions);
|
|
2762
2787
|
matchUtilities({
|
|
2763
2788
|
to: (value)=>({
|
|
2764
|
-
"--tw-gradient-to": (0, _toColorValue.default)(value, "to")
|
|
2789
|
+
"--tw-gradient-to": `${(0, _toColorValue.default)(value, "to")} var(--tw-gradient-to-position)`,
|
|
2790
|
+
"--tw-gradient-to-position": " "
|
|
2765
2791
|
})
|
|
2766
2792
|
}, options);
|
|
2793
|
+
matchUtilities({
|
|
2794
|
+
to: (value)=>{
|
|
2795
|
+
return {
|
|
2796
|
+
"--tw-gradient-to-position": value
|
|
2797
|
+
};
|
|
2798
|
+
}
|
|
2799
|
+
}, positionOptions);
|
|
2767
2800
|
};
|
|
2768
2801
|
})(),
|
|
2769
2802
|
boxDecorationBreak: ({ addUtilities })=>{
|
|
@@ -11,6 +11,7 @@ const _postcssSelectorParser = /*#__PURE__*/ _interopRequireDefault(require("pos
|
|
|
11
11
|
const _generateRules = require("./generateRules");
|
|
12
12
|
const _escapeClassName = /*#__PURE__*/ _interopRequireDefault(require("../util/escapeClassName"));
|
|
13
13
|
const _applyImportantSelector = require("../util/applyImportantSelector");
|
|
14
|
+
const _pseudoElements = require("../util/pseudoElements");
|
|
14
15
|
function _interopRequireDefault(obj) {
|
|
15
16
|
return obj && obj.__esModule ? obj : {
|
|
16
17
|
default: obj
|
|
@@ -487,6 +488,10 @@ function processApply(root, context, localCache) {
|
|
|
487
488
|
rule.walkDecls((d)=>{
|
|
488
489
|
d.important = meta.important || important1;
|
|
489
490
|
});
|
|
491
|
+
// Move pseudo elements to the end of the selector (if necessary)
|
|
492
|
+
let selector = (0, _postcssSelectorParser.default)().astSync(rule.selector);
|
|
493
|
+
selector.each((sel)=>(0, _pseudoElements.movePseudos)(sel));
|
|
494
|
+
rule.selector = selector.toString();
|
|
490
495
|
});
|
|
491
496
|
}
|
|
492
497
|
// It could be that the node we were inserted was removed because the class didn't match
|
package/lib/lib/load-config.js
CHANGED
|
@@ -28,9 +28,13 @@ function lazyJiti() {
|
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
function loadConfig(path) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
let config = function() {
|
|
32
|
+
try {
|
|
33
|
+
return path ? require(path) : {};
|
|
34
|
+
} catch {
|
|
35
|
+
return lazyJiti()(path);
|
|
36
|
+
}
|
|
37
|
+
}();
|
|
38
|
+
var _config_default;
|
|
39
|
+
return (_config_default = config.default) !== null && _config_default !== void 0 ? _config_default : config;
|
|
36
40
|
}
|
package/lib/lib/sharedState.js
CHANGED
|
@@ -25,11 +25,16 @@ function _interopRequireDefault(obj) {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
let OXIDE_DEFAULT_ENABLED = _packageJson.default.tailwindcss.engine === "oxide";
|
|
28
|
-
const env = {
|
|
28
|
+
const env = typeof process !== "undefined" ? {
|
|
29
29
|
NODE_ENV: process.env.NODE_ENV,
|
|
30
30
|
DEBUG: resolveDebug(process.env.DEBUG),
|
|
31
31
|
ENGINE: _packageJson.default.tailwindcss.engine,
|
|
32
32
|
OXIDE: resolveBoolean(process.env.OXIDE, OXIDE_DEFAULT_ENABLED)
|
|
33
|
+
} : {
|
|
34
|
+
NODE_ENV: "production",
|
|
35
|
+
DEBUG: false,
|
|
36
|
+
ENGINE: _packageJson.default.tailwindcss.engine,
|
|
37
|
+
OXIDE: OXIDE_DEFAULT_ENABLED
|
|
33
38
|
};
|
|
34
39
|
const contextMap = new Map();
|
|
35
40
|
const configContextMap = new Map();
|
|
@@ -6,17 +6,29 @@ Object.defineProperty(exports, "applyImportantSelector", {
|
|
|
6
6
|
enumerable: true,
|
|
7
7
|
get: ()=>applyImportantSelector
|
|
8
8
|
});
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
const _postcssSelectorParser = /*#__PURE__*/ _interopRequireDefault(require("postcss-selector-parser"));
|
|
10
|
+
const _pseudoElements = require("./pseudoElements");
|
|
11
|
+
function _interopRequireDefault(obj) {
|
|
12
|
+
return obj && obj.__esModule ? obj : {
|
|
13
|
+
default: obj
|
|
14
|
+
};
|
|
15
15
|
}
|
|
16
|
-
function
|
|
17
|
-
let
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
function applyImportantSelector(selector, important) {
|
|
17
|
+
let sel = (0, _postcssSelectorParser.default)().astSync(selector);
|
|
18
|
+
sel.each((sel)=>{
|
|
19
|
+
// Wrap with :is if it's not already wrapped
|
|
20
|
+
let isWrapped = sel.nodes[0].type === "pseudo" && sel.nodes[0].value === ":is" && sel.nodes.every((node)=>node.type !== "combinator");
|
|
21
|
+
if (!isWrapped) {
|
|
22
|
+
sel.nodes = [
|
|
23
|
+
_postcssSelectorParser.default.pseudo({
|
|
24
|
+
value: ":is",
|
|
25
|
+
nodes: [
|
|
26
|
+
sel.clone()
|
|
27
|
+
]
|
|
28
|
+
})
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
(0, _pseudoElements.movePseudos)(sel);
|
|
32
|
+
});
|
|
33
|
+
return `${important} ${sel.toString()}`;
|
|
22
34
|
}
|
|
@@ -18,6 +18,7 @@ const _postcssSelectorParser = /*#__PURE__*/ _interopRequireDefault(require("pos
|
|
|
18
18
|
const _unesc = /*#__PURE__*/ _interopRequireDefault(require("postcss-selector-parser/dist/util/unesc"));
|
|
19
19
|
const _escapeClassName = /*#__PURE__*/ _interopRequireDefault(require("../util/escapeClassName"));
|
|
20
20
|
const _prefixSelector = /*#__PURE__*/ _interopRequireDefault(require("../util/prefixSelector"));
|
|
21
|
+
const _pseudoElements = require("./pseudoElements");
|
|
21
22
|
function _interopRequireDefault(obj) {
|
|
22
23
|
return obj && obj.__esModule ? obj : {
|
|
23
24
|
default: obj
|
|
@@ -201,12 +202,7 @@ function finalizeSelector(current, formats, { context , candidate , base }) {
|
|
|
201
202
|
}
|
|
202
203
|
});
|
|
203
204
|
// Move pseudo elements to the end of the selector (if necessary)
|
|
204
|
-
selector.each((sel)=>
|
|
205
|
-
let pseudoElements = collectPseudoElements(sel);
|
|
206
|
-
if (pseudoElements.length > 0) {
|
|
207
|
-
sel.nodes.push(pseudoElements.sort(sortSelector));
|
|
208
|
-
}
|
|
209
|
-
});
|
|
205
|
+
selector.each((sel)=>(0, _pseudoElements.movePseudos)(sel));
|
|
210
206
|
return selector.toString();
|
|
211
207
|
}
|
|
212
208
|
function handleMergePseudo(selector, format) {
|
|
@@ -257,85 +253,3 @@ function handleMergePseudo(selector, format) {
|
|
|
257
253
|
format
|
|
258
254
|
];
|
|
259
255
|
}
|
|
260
|
-
// Note: As a rule, double colons (::) should be used instead of a single colon
|
|
261
|
-
// (:). This distinguishes pseudo-classes from pseudo-elements. However, since
|
|
262
|
-
// this distinction was not present in older versions of the W3C spec, most
|
|
263
|
-
// browsers support both syntaxes for the original pseudo-elements.
|
|
264
|
-
let pseudoElementsBC = [
|
|
265
|
-
":before",
|
|
266
|
-
":after",
|
|
267
|
-
":first-line",
|
|
268
|
-
":first-letter"
|
|
269
|
-
];
|
|
270
|
-
// These pseudo-elements _can_ be combined with other pseudo selectors AND the order does matter.
|
|
271
|
-
let pseudoElementExceptions = [
|
|
272
|
-
"::file-selector-button",
|
|
273
|
-
// Webkit scroll bar pseudo elements can be combined with user-action pseudo classes
|
|
274
|
-
"::-webkit-scrollbar",
|
|
275
|
-
"::-webkit-scrollbar-button",
|
|
276
|
-
"::-webkit-scrollbar-thumb",
|
|
277
|
-
"::-webkit-scrollbar-track",
|
|
278
|
-
"::-webkit-scrollbar-track-piece",
|
|
279
|
-
"::-webkit-scrollbar-corner",
|
|
280
|
-
"::-webkit-resizer"
|
|
281
|
-
];
|
|
282
|
-
/**
|
|
283
|
-
* This will make sure to move pseudo's to the correct spot (the end for
|
|
284
|
-
* pseudo elements) because otherwise the selector will never work
|
|
285
|
-
* anyway.
|
|
286
|
-
*
|
|
287
|
-
* E.g.:
|
|
288
|
-
* - `before:hover:text-center` would result in `.before\:hover\:text-center:hover::before`
|
|
289
|
-
* - `hover:before:text-center` would result in `.hover\:before\:text-center:hover::before`
|
|
290
|
-
*
|
|
291
|
-
* `::before:hover` doesn't work, which means that we can make it work for you by flipping the order.
|
|
292
|
-
*
|
|
293
|
-
* @param {Selector} selector
|
|
294
|
-
**/ function collectPseudoElements(selector) {
|
|
295
|
-
/** @type {Node[]} */ let nodes = [];
|
|
296
|
-
for (let node of selector.nodes){
|
|
297
|
-
if (isPseudoElement(node)) {
|
|
298
|
-
nodes.push(node);
|
|
299
|
-
selector.removeChild(node);
|
|
300
|
-
}
|
|
301
|
-
if (node === null || node === void 0 ? void 0 : node.nodes) {
|
|
302
|
-
nodes.push(...collectPseudoElements(node));
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
return nodes;
|
|
306
|
-
}
|
|
307
|
-
// This will make sure to move pseudo's to the correct spot (the end for
|
|
308
|
-
// pseudo elements) because otherwise the selector will never work
|
|
309
|
-
// anyway.
|
|
310
|
-
//
|
|
311
|
-
// E.g.:
|
|
312
|
-
// - `before:hover:text-center` would result in `.before\:hover\:text-center:hover::before`
|
|
313
|
-
// - `hover:before:text-center` would result in `.hover\:before\:text-center:hover::before`
|
|
314
|
-
//
|
|
315
|
-
// `::before:hover` doesn't work, which means that we can make it work
|
|
316
|
-
// for you by flipping the order.
|
|
317
|
-
function sortSelector(a, z) {
|
|
318
|
-
// Both nodes are non-pseudo's so we can safely ignore them and keep
|
|
319
|
-
// them in the same order.
|
|
320
|
-
if (a.type !== "pseudo" && z.type !== "pseudo") {
|
|
321
|
-
return 0;
|
|
322
|
-
}
|
|
323
|
-
// If one of them is a combinator, we need to keep it in the same order
|
|
324
|
-
// because that means it will start a new "section" in the selector.
|
|
325
|
-
if (a.type === "combinator" ^ z.type === "combinator") {
|
|
326
|
-
return 0;
|
|
327
|
-
}
|
|
328
|
-
// One of the items is a pseudo and the other one isn't. Let's move
|
|
329
|
-
// the pseudo to the right.
|
|
330
|
-
if (a.type === "pseudo" ^ z.type === "pseudo") {
|
|
331
|
-
return (a.type === "pseudo") - (z.type === "pseudo");
|
|
332
|
-
}
|
|
333
|
-
// Both are pseudo's, move the pseudo elements (except for
|
|
334
|
-
// ::file-selector-button) to the right.
|
|
335
|
-
return isPseudoElement(a) - isPseudoElement(z);
|
|
336
|
-
}
|
|
337
|
-
function isPseudoElement(node) {
|
|
338
|
-
if (node.type !== "pseudo") return false;
|
|
339
|
-
if (pseudoElementExceptions.includes(node.value)) return false;
|
|
340
|
-
return node.value.startsWith("::") || pseudoElementsBC.includes(node.value);
|
|
341
|
-
}
|
|
@@ -276,19 +276,5 @@ function normalizeConfig(config) {
|
|
|
276
276
|
break;
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
|
-
// Warn if the line-clamp plugin is installed
|
|
280
|
-
if (config.plugins.length > 0) {
|
|
281
|
-
let plugin;
|
|
282
|
-
try {
|
|
283
|
-
plugin = require("@tailwindcss/line-clamp");
|
|
284
|
-
} catch {}
|
|
285
|
-
if (plugin && config.plugins.includes(plugin)) {
|
|
286
|
-
_log.default.warn("line-clamp-in-core", [
|
|
287
|
-
"As of Tailwind CSS v3.3, the `@tailwindcss/line-clamp` plugin is now included by default.",
|
|
288
|
-
"Remove it from the `plugins` array in your configuration to eliminate this warning."
|
|
289
|
-
]);
|
|
290
|
-
config.plugins = config.plugins.filter((p)=>p !== plugin);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
279
|
return config;
|
|
294
280
|
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/** @typedef {import('postcss-selector-parser').Root} Root */ /** @typedef {import('postcss-selector-parser').Selector} Selector */ /** @typedef {import('postcss-selector-parser').Pseudo} Pseudo */ /** @typedef {import('postcss-selector-parser').Node} Node */ // There are some pseudo-elements that may or may not be:
|
|
2
|
+
// **Actionable**
|
|
3
|
+
// Zero or more user-action pseudo-classes may be attached to the pseudo-element itself
|
|
4
|
+
// structural-pseudo-classes are NOT allowed but we don't make
|
|
5
|
+
// The spec is not clear on whether this is allowed or not — but in practice it is.
|
|
6
|
+
// **Terminal**
|
|
7
|
+
// It MUST be placed at the end of a selector
|
|
8
|
+
//
|
|
9
|
+
// This is the required in the spec. However, some pseudo elements are not "terminal" because
|
|
10
|
+
// they represent a "boundary piercing" that is compiled out by a build step.
|
|
11
|
+
// **Jumpable**
|
|
12
|
+
// Any terminal element may "jump" over combinators when moving to the end of the selector
|
|
13
|
+
//
|
|
14
|
+
// This is a backwards-compat quirk of :before and :after variants.
|
|
15
|
+
/** @typedef {'terminal' | 'actionable' | 'jumpable'} PseudoProperty */ /** @type {Record<string, PseudoProperty[]>} */ "use strict";
|
|
16
|
+
Object.defineProperty(exports, "__esModule", {
|
|
17
|
+
value: true
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports, "movePseudos", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
get: ()=>movePseudos
|
|
22
|
+
});
|
|
23
|
+
let elementProperties = {
|
|
24
|
+
"::after": [
|
|
25
|
+
"terminal",
|
|
26
|
+
"jumpable"
|
|
27
|
+
],
|
|
28
|
+
"::backdrop": [
|
|
29
|
+
"terminal"
|
|
30
|
+
],
|
|
31
|
+
"::before": [
|
|
32
|
+
"terminal",
|
|
33
|
+
"jumpable"
|
|
34
|
+
],
|
|
35
|
+
"::cue": [
|
|
36
|
+
"terminal"
|
|
37
|
+
],
|
|
38
|
+
"::cue-region": [
|
|
39
|
+
"terminal"
|
|
40
|
+
],
|
|
41
|
+
"::first-letter": [
|
|
42
|
+
"terminal",
|
|
43
|
+
"jumpable"
|
|
44
|
+
],
|
|
45
|
+
"::first-line": [
|
|
46
|
+
"terminal",
|
|
47
|
+
"jumpable"
|
|
48
|
+
],
|
|
49
|
+
"::grammar-error": [
|
|
50
|
+
"terminal"
|
|
51
|
+
],
|
|
52
|
+
"::marker": [
|
|
53
|
+
"terminal"
|
|
54
|
+
],
|
|
55
|
+
"::part": [
|
|
56
|
+
"terminal",
|
|
57
|
+
"actionable"
|
|
58
|
+
],
|
|
59
|
+
"::placeholder": [
|
|
60
|
+
"terminal"
|
|
61
|
+
],
|
|
62
|
+
"::selection": [
|
|
63
|
+
"terminal"
|
|
64
|
+
],
|
|
65
|
+
"::slotted": [
|
|
66
|
+
"terminal"
|
|
67
|
+
],
|
|
68
|
+
"::spelling-error": [
|
|
69
|
+
"terminal"
|
|
70
|
+
],
|
|
71
|
+
"::target-text": [
|
|
72
|
+
"terminal"
|
|
73
|
+
],
|
|
74
|
+
// other
|
|
75
|
+
"::file-selector-button": [
|
|
76
|
+
"terminal",
|
|
77
|
+
"actionable"
|
|
78
|
+
],
|
|
79
|
+
"::-webkit-progress-bar": [
|
|
80
|
+
"terminal",
|
|
81
|
+
"actionable"
|
|
82
|
+
],
|
|
83
|
+
// Webkit scroll bar pseudo elements can be combined with user-action pseudo classes
|
|
84
|
+
"::-webkit-scrollbar": [
|
|
85
|
+
"terminal",
|
|
86
|
+
"actionable"
|
|
87
|
+
],
|
|
88
|
+
"::-webkit-scrollbar-button": [
|
|
89
|
+
"terminal",
|
|
90
|
+
"actionable"
|
|
91
|
+
],
|
|
92
|
+
"::-webkit-scrollbar-thumb": [
|
|
93
|
+
"terminal",
|
|
94
|
+
"actionable"
|
|
95
|
+
],
|
|
96
|
+
"::-webkit-scrollbar-track": [
|
|
97
|
+
"terminal",
|
|
98
|
+
"actionable"
|
|
99
|
+
],
|
|
100
|
+
"::-webkit-scrollbar-track-piece": [
|
|
101
|
+
"terminal",
|
|
102
|
+
"actionable"
|
|
103
|
+
],
|
|
104
|
+
"::-webkit-scrollbar-corner": [
|
|
105
|
+
"terminal",
|
|
106
|
+
"actionable"
|
|
107
|
+
],
|
|
108
|
+
"::-webkit-resizer": [
|
|
109
|
+
"terminal",
|
|
110
|
+
"actionable"
|
|
111
|
+
],
|
|
112
|
+
// Note: As a rule, double colons (::) should be used instead of a single colon
|
|
113
|
+
// (:). This distinguishes pseudo-classes from pseudo-elements. However, since
|
|
114
|
+
// this distinction was not present in older versions of the W3C spec, most
|
|
115
|
+
// browsers support both syntaxes for the original pseudo-elements.
|
|
116
|
+
":after": [
|
|
117
|
+
"terminal",
|
|
118
|
+
"jumpable"
|
|
119
|
+
],
|
|
120
|
+
":before": [
|
|
121
|
+
"terminal",
|
|
122
|
+
"jumpable"
|
|
123
|
+
],
|
|
124
|
+
":first-letter": [
|
|
125
|
+
"terminal",
|
|
126
|
+
"jumpable"
|
|
127
|
+
],
|
|
128
|
+
":first-line": [
|
|
129
|
+
"terminal",
|
|
130
|
+
"jumpable"
|
|
131
|
+
],
|
|
132
|
+
// The default value is used when the pseudo-element is not recognized
|
|
133
|
+
// Because it's not recognized, we don't know if it's terminal or not
|
|
134
|
+
// So we assume it can't be moved AND can have user-action pseudo classes attached to it
|
|
135
|
+
__default__: [
|
|
136
|
+
"actionable"
|
|
137
|
+
]
|
|
138
|
+
};
|
|
139
|
+
function movePseudos(sel) {
|
|
140
|
+
let [pseudos] = movablePseudos(sel);
|
|
141
|
+
// Remove all pseudo elements from their respective selectors
|
|
142
|
+
pseudos.forEach(([sel, pseudo])=>sel.removeChild(pseudo));
|
|
143
|
+
// Re-add them to the end of the selector in the correct order.
|
|
144
|
+
// This moves terminal pseudo elements to the end of the
|
|
145
|
+
// selector otherwise the selector will not be valid.
|
|
146
|
+
//
|
|
147
|
+
// Examples:
|
|
148
|
+
// - `before:hover:text-center` would result in `.before\:hover\:text-center:hover::before`
|
|
149
|
+
// - `hover:before:text-center` would result in `.hover\:before\:text-center:hover::before`
|
|
150
|
+
//
|
|
151
|
+
// The selector `::before:hover` does not work but we
|
|
152
|
+
// can make it work for you by flipping the order.
|
|
153
|
+
sel.nodes.push(...pseudos.map(([, pseudo])=>pseudo));
|
|
154
|
+
return sel;
|
|
155
|
+
}
|
|
156
|
+
/** @typedef {[sel: Selector, pseudo: Pseudo, attachedTo: Pseudo | null]} MovablePseudo */ /** @typedef {[pseudos: MovablePseudo[], lastSeenElement: Pseudo | null]} MovablePseudosResult */ /**
|
|
157
|
+
* @param {Selector} sel
|
|
158
|
+
* @returns {MovablePseudosResult}
|
|
159
|
+
*/ function movablePseudos(sel) {
|
|
160
|
+
/** @type {MovablePseudo[]} */ let buffer = [];
|
|
161
|
+
/** @type {Pseudo | null} */ let lastSeenElement = null;
|
|
162
|
+
for (let node of sel.nodes){
|
|
163
|
+
if (node.type === "combinator") {
|
|
164
|
+
buffer = buffer.filter(([, node])=>propertiesForPseudo(node).includes("jumpable"));
|
|
165
|
+
lastSeenElement = null;
|
|
166
|
+
} else if (node.type === "pseudo") {
|
|
167
|
+
if (isMovablePseudoElement(node)) {
|
|
168
|
+
lastSeenElement = node;
|
|
169
|
+
buffer.push([
|
|
170
|
+
sel,
|
|
171
|
+
node,
|
|
172
|
+
null
|
|
173
|
+
]);
|
|
174
|
+
} else if (lastSeenElement && isAttachablePseudoClass(node, lastSeenElement)) {
|
|
175
|
+
buffer.push([
|
|
176
|
+
sel,
|
|
177
|
+
node,
|
|
178
|
+
lastSeenElement
|
|
179
|
+
]);
|
|
180
|
+
} else {
|
|
181
|
+
lastSeenElement = null;
|
|
182
|
+
}
|
|
183
|
+
var _node_nodes;
|
|
184
|
+
for (let sub of (_node_nodes = node.nodes) !== null && _node_nodes !== void 0 ? _node_nodes : []){
|
|
185
|
+
let [movable, lastSeenElementInSub] = movablePseudos(sub);
|
|
186
|
+
lastSeenElement = lastSeenElementInSub || lastSeenElement;
|
|
187
|
+
buffer.push(...movable);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return [
|
|
192
|
+
buffer,
|
|
193
|
+
lastSeenElement
|
|
194
|
+
];
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* @param {Node} node
|
|
198
|
+
* @returns {boolean}
|
|
199
|
+
*/ function isPseudoElement(node) {
|
|
200
|
+
return node.value.startsWith("::") || elementProperties[node.value] !== undefined;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* @param {Node} node
|
|
204
|
+
* @returns {boolean}
|
|
205
|
+
*/ function isMovablePseudoElement(node) {
|
|
206
|
+
return isPseudoElement(node) && propertiesForPseudo(node).includes("terminal");
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* @param {Node} node
|
|
210
|
+
* @param {Pseudo} pseudo
|
|
211
|
+
* @returns {boolean}
|
|
212
|
+
*/ function isAttachablePseudoClass(node, pseudo) {
|
|
213
|
+
if (node.type !== "pseudo") return false;
|
|
214
|
+
if (isPseudoElement(node)) return false;
|
|
215
|
+
return propertiesForPseudo(pseudo).includes("actionable");
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* @param {Pseudo} pseudo
|
|
219
|
+
* @returns {PseudoProperty[]}
|
|
220
|
+
*/ function propertiesForPseudo(pseudo) {
|
|
221
|
+
var _elementProperties_pseudo_value;
|
|
222
|
+
return (_elementProperties_pseudo_value = elementProperties[pseudo.value]) !== null && _elementProperties_pseudo_value !== void 0 ? _elementProperties_pseudo_value : elementProperties.__default__;
|
|
223
|
+
}
|
|
@@ -20,5 +20,16 @@ function validateConfig(config) {
|
|
|
20
20
|
"https://tailwindcss.com/docs/content-configuration"
|
|
21
21
|
]);
|
|
22
22
|
}
|
|
23
|
+
// Warn if the line-clamp plugin is installed
|
|
24
|
+
try {
|
|
25
|
+
let plugin = require("@tailwindcss/line-clamp");
|
|
26
|
+
if (config.plugins.includes(plugin)) {
|
|
27
|
+
_log.default.warn("line-clamp-in-core", [
|
|
28
|
+
"As of Tailwind CSS v3.3, the `@tailwindcss/line-clamp` plugin is now included by default.",
|
|
29
|
+
"Remove it from the `plugins` array in your configuration to eliminate this warning."
|
|
30
|
+
]);
|
|
31
|
+
config.plugins = config.plugins.filter((p)=>p !== plugin);
|
|
32
|
+
}
|
|
33
|
+
} catch {}
|
|
23
34
|
return config;
|
|
24
35
|
}
|
package/package.json
CHANGED
package/src/corePlugins.js
CHANGED
|
@@ -1756,40 +1756,92 @@ export let corePlugins = {
|
|
|
1756
1756
|
type: ['color', 'any'],
|
|
1757
1757
|
}
|
|
1758
1758
|
|
|
1759
|
+
let positionOptions = {
|
|
1760
|
+
values: theme('gradientColorStopPositions'),
|
|
1761
|
+
type: ['length', 'percentage'],
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1759
1764
|
matchUtilities(
|
|
1760
1765
|
{
|
|
1761
1766
|
from: (value) => {
|
|
1762
1767
|
let transparentToValue = transparentTo(value)
|
|
1763
1768
|
|
|
1764
1769
|
return {
|
|
1765
|
-
'--tw-gradient-from': toColorValue(
|
|
1766
|
-
|
|
1770
|
+
'--tw-gradient-from': `${toColorValue(
|
|
1771
|
+
value,
|
|
1772
|
+
'from'
|
|
1773
|
+
)} var(--tw-gradient-from-position)`,
|
|
1774
|
+
'--tw-gradient-from-position': ' ',
|
|
1775
|
+
'--tw-gradient-to': `${transparentToValue} var(--tw-gradient-from-position)`,
|
|
1776
|
+
'--tw-gradient-to-position': ' ',
|
|
1767
1777
|
'--tw-gradient-stops': `var(--tw-gradient-from), var(--tw-gradient-to)`,
|
|
1768
1778
|
}
|
|
1769
1779
|
},
|
|
1770
1780
|
},
|
|
1771
1781
|
options
|
|
1772
1782
|
)
|
|
1783
|
+
|
|
1784
|
+
matchUtilities(
|
|
1785
|
+
{
|
|
1786
|
+
from: (value) => {
|
|
1787
|
+
return {
|
|
1788
|
+
'--tw-gradient-from-position': value,
|
|
1789
|
+
}
|
|
1790
|
+
},
|
|
1791
|
+
},
|
|
1792
|
+
positionOptions
|
|
1793
|
+
)
|
|
1794
|
+
|
|
1773
1795
|
matchUtilities(
|
|
1774
1796
|
{
|
|
1775
1797
|
via: (value) => {
|
|
1776
1798
|
let transparentToValue = transparentTo(value)
|
|
1777
1799
|
|
|
1778
1800
|
return {
|
|
1779
|
-
'--tw-gradient-
|
|
1801
|
+
'--tw-gradient-via-position': ' ',
|
|
1802
|
+
'--tw-gradient-to': `${transparentToValue} var(--tw-gradient-to-position)`,
|
|
1803
|
+
'--tw-gradient-to-position': ' ',
|
|
1780
1804
|
'--tw-gradient-stops': `var(--tw-gradient-from), ${toColorValue(
|
|
1781
1805
|
value,
|
|
1782
1806
|
'via'
|
|
1783
|
-
)}, var(--tw-gradient-to)`,
|
|
1807
|
+
)} var(--tw-gradient-via-position), var(--tw-gradient-to)`,
|
|
1784
1808
|
}
|
|
1785
1809
|
},
|
|
1786
1810
|
},
|
|
1787
1811
|
options
|
|
1788
1812
|
)
|
|
1813
|
+
|
|
1789
1814
|
matchUtilities(
|
|
1790
|
-
{
|
|
1815
|
+
{
|
|
1816
|
+
via: (value) => {
|
|
1817
|
+
return {
|
|
1818
|
+
'--tw-gradient-via-position': value,
|
|
1819
|
+
}
|
|
1820
|
+
},
|
|
1821
|
+
},
|
|
1822
|
+
positionOptions
|
|
1823
|
+
)
|
|
1824
|
+
|
|
1825
|
+
matchUtilities(
|
|
1826
|
+
{
|
|
1827
|
+
to: (value) => ({
|
|
1828
|
+
'--tw-gradient-to': `${toColorValue(value, 'to')} var(--tw-gradient-to-position)`,
|
|
1829
|
+
'--tw-gradient-to-position': ' ',
|
|
1830
|
+
}),
|
|
1831
|
+
},
|
|
1791
1832
|
options
|
|
1792
1833
|
)
|
|
1834
|
+
|
|
1835
|
+
matchUtilities(
|
|
1836
|
+
{
|
|
1837
|
+
to: (value) => {
|
|
1838
|
+
return {
|
|
1839
|
+
'--tw-gradient-to-position': value,
|
|
1840
|
+
}
|
|
1841
|
+
},
|
|
1842
|
+
},
|
|
1843
|
+
positionOptions
|
|
1844
|
+
)
|
|
1793
1845
|
}
|
|
1794
1846
|
})(),
|
|
1795
1847
|
|
|
@@ -4,6 +4,7 @@ import parser from 'postcss-selector-parser'
|
|
|
4
4
|
import { resolveMatches } from './generateRules'
|
|
5
5
|
import escapeClassName from '../util/escapeClassName'
|
|
6
6
|
import { applyImportantSelector } from '../util/applyImportantSelector'
|
|
7
|
+
import { movePseudos } from '../util/pseudoElements'
|
|
7
8
|
|
|
8
9
|
/** @typedef {Map<string, [any, import('postcss').Rule[]]>} ApplyCache */
|
|
9
10
|
|
|
@@ -562,6 +563,11 @@ function processApply(root, context, localCache) {
|
|
|
562
563
|
rule.walkDecls((d) => {
|
|
563
564
|
d.important = meta.important || important
|
|
564
565
|
})
|
|
566
|
+
|
|
567
|
+
// Move pseudo elements to the end of the selector (if necessary)
|
|
568
|
+
let selector = parser().astSync(rule.selector)
|
|
569
|
+
selector.each((sel) => movePseudos(sel))
|
|
570
|
+
rule.selector = selector.toString()
|
|
565
571
|
})
|
|
566
572
|
}
|
|
567
573
|
|
package/src/lib/load-config.ts
CHANGED
|
@@ -19,9 +19,13 @@ function lazyJiti() {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function loadConfig(path: string): Config {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
let config = (function () {
|
|
23
|
+
try {
|
|
24
|
+
return path ? require(path) : {}
|
|
25
|
+
} catch {
|
|
26
|
+
return lazyJiti()(path)
|
|
27
|
+
}
|
|
28
|
+
})()
|
|
29
|
+
|
|
30
|
+
return config.default ?? config
|
|
27
31
|
}
|
package/src/lib/sharedState.js
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import pkg from '../../package.json'
|
|
2
2
|
let OXIDE_DEFAULT_ENABLED = pkg.tailwindcss.engine === 'oxide'
|
|
3
3
|
|
|
4
|
-
export const env =
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
export const env =
|
|
5
|
+
typeof process !== 'undefined'
|
|
6
|
+
? {
|
|
7
|
+
NODE_ENV: process.env.NODE_ENV,
|
|
8
|
+
DEBUG: resolveDebug(process.env.DEBUG),
|
|
9
|
+
ENGINE: pkg.tailwindcss.engine,
|
|
10
|
+
OXIDE: resolveBoolean(process.env.OXIDE, OXIDE_DEFAULT_ENABLED),
|
|
11
|
+
}
|
|
12
|
+
: {
|
|
13
|
+
NODE_ENV: 'production',
|
|
14
|
+
DEBUG: false,
|
|
15
|
+
ENGINE: pkg.tailwindcss.engine,
|
|
16
|
+
OXIDE: OXIDE_DEFAULT_ENABLED,
|
|
17
|
+
}
|
|
18
|
+
|
|
10
19
|
export const contextMap = new Map()
|
|
11
20
|
export const configContextMap = new Map()
|
|
12
21
|
export const contextSourcesMap = new Map()
|
|
@@ -1,19 +1,27 @@
|
|
|
1
|
-
import
|
|
1
|
+
import parser from 'postcss-selector-parser'
|
|
2
|
+
import { movePseudos } from './pseudoElements'
|
|
2
3
|
|
|
3
4
|
export function applyImportantSelector(selector, important) {
|
|
4
|
-
let
|
|
5
|
-
if (!matches) return `${important} ${wrapWithIs(selector)}`
|
|
5
|
+
let sel = parser().astSync(selector)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
sel.each((sel) => {
|
|
8
|
+
// Wrap with :is if it's not already wrapped
|
|
9
|
+
let isWrapped =
|
|
10
|
+
sel.nodes[0].type === 'pseudo' &&
|
|
11
|
+
sel.nodes[0].value === ':is' &&
|
|
12
|
+
sel.nodes.every((node) => node.type !== 'combinator')
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
if (!isWrapped) {
|
|
15
|
+
sel.nodes = [
|
|
16
|
+
parser.pseudo({
|
|
17
|
+
value: ':is',
|
|
18
|
+
nodes: [sel.clone()],
|
|
19
|
+
}),
|
|
20
|
+
]
|
|
21
|
+
}
|
|
13
22
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
23
|
+
movePseudos(sel)
|
|
24
|
+
})
|
|
17
25
|
|
|
18
|
-
return
|
|
26
|
+
return `${important} ${sel.toString()}`
|
|
19
27
|
}
|
|
@@ -2,6 +2,7 @@ import selectorParser from 'postcss-selector-parser'
|
|
|
2
2
|
import unescape from 'postcss-selector-parser/dist/util/unesc'
|
|
3
3
|
import escapeClassName from '../util/escapeClassName'
|
|
4
4
|
import prefixSelector from '../util/prefixSelector'
|
|
5
|
+
import { movePseudos } from './pseudoElements'
|
|
5
6
|
|
|
6
7
|
/** @typedef {import('postcss-selector-parser').Root} Root */
|
|
7
8
|
/** @typedef {import('postcss-selector-parser').Selector} Selector */
|
|
@@ -245,12 +246,7 @@ export function finalizeSelector(current, formats, { context, candidate, base })
|
|
|
245
246
|
})
|
|
246
247
|
|
|
247
248
|
// Move pseudo elements to the end of the selector (if necessary)
|
|
248
|
-
selector.each((sel) =>
|
|
249
|
-
let pseudoElements = collectPseudoElements(sel)
|
|
250
|
-
if (pseudoElements.length > 0) {
|
|
251
|
-
sel.nodes.push(pseudoElements.sort(sortSelector))
|
|
252
|
-
}
|
|
253
|
-
})
|
|
249
|
+
selector.each((sel) => movePseudos(sel))
|
|
254
250
|
|
|
255
251
|
return selector.toString()
|
|
256
252
|
}
|
|
@@ -318,95 +314,3 @@ export function handleMergePseudo(selector, format) {
|
|
|
318
314
|
|
|
319
315
|
return [selector, format]
|
|
320
316
|
}
|
|
321
|
-
|
|
322
|
-
// Note: As a rule, double colons (::) should be used instead of a single colon
|
|
323
|
-
// (:). This distinguishes pseudo-classes from pseudo-elements. However, since
|
|
324
|
-
// this distinction was not present in older versions of the W3C spec, most
|
|
325
|
-
// browsers support both syntaxes for the original pseudo-elements.
|
|
326
|
-
let pseudoElementsBC = [':before', ':after', ':first-line', ':first-letter']
|
|
327
|
-
|
|
328
|
-
// These pseudo-elements _can_ be combined with other pseudo selectors AND the order does matter.
|
|
329
|
-
let pseudoElementExceptions = [
|
|
330
|
-
'::file-selector-button',
|
|
331
|
-
|
|
332
|
-
// Webkit scroll bar pseudo elements can be combined with user-action pseudo classes
|
|
333
|
-
'::-webkit-scrollbar',
|
|
334
|
-
'::-webkit-scrollbar-button',
|
|
335
|
-
'::-webkit-scrollbar-thumb',
|
|
336
|
-
'::-webkit-scrollbar-track',
|
|
337
|
-
'::-webkit-scrollbar-track-piece',
|
|
338
|
-
'::-webkit-scrollbar-corner',
|
|
339
|
-
'::-webkit-resizer',
|
|
340
|
-
]
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* This will make sure to move pseudo's to the correct spot (the end for
|
|
344
|
-
* pseudo elements) because otherwise the selector will never work
|
|
345
|
-
* anyway.
|
|
346
|
-
*
|
|
347
|
-
* E.g.:
|
|
348
|
-
* - `before:hover:text-center` would result in `.before\:hover\:text-center:hover::before`
|
|
349
|
-
* - `hover:before:text-center` would result in `.hover\:before\:text-center:hover::before`
|
|
350
|
-
*
|
|
351
|
-
* `::before:hover` doesn't work, which means that we can make it work for you by flipping the order.
|
|
352
|
-
*
|
|
353
|
-
* @param {Selector} selector
|
|
354
|
-
**/
|
|
355
|
-
function collectPseudoElements(selector) {
|
|
356
|
-
/** @type {Node[]} */
|
|
357
|
-
let nodes = []
|
|
358
|
-
|
|
359
|
-
for (let node of selector.nodes) {
|
|
360
|
-
if (isPseudoElement(node)) {
|
|
361
|
-
nodes.push(node)
|
|
362
|
-
selector.removeChild(node)
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
if (node?.nodes) {
|
|
366
|
-
nodes.push(...collectPseudoElements(node))
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
return nodes
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// This will make sure to move pseudo's to the correct spot (the end for
|
|
374
|
-
// pseudo elements) because otherwise the selector will never work
|
|
375
|
-
// anyway.
|
|
376
|
-
//
|
|
377
|
-
// E.g.:
|
|
378
|
-
// - `before:hover:text-center` would result in `.before\:hover\:text-center:hover::before`
|
|
379
|
-
// - `hover:before:text-center` would result in `.hover\:before\:text-center:hover::before`
|
|
380
|
-
//
|
|
381
|
-
// `::before:hover` doesn't work, which means that we can make it work
|
|
382
|
-
// for you by flipping the order.
|
|
383
|
-
function sortSelector(a, z) {
|
|
384
|
-
// Both nodes are non-pseudo's so we can safely ignore them and keep
|
|
385
|
-
// them in the same order.
|
|
386
|
-
if (a.type !== 'pseudo' && z.type !== 'pseudo') {
|
|
387
|
-
return 0
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// If one of them is a combinator, we need to keep it in the same order
|
|
391
|
-
// because that means it will start a new "section" in the selector.
|
|
392
|
-
if ((a.type === 'combinator') ^ (z.type === 'combinator')) {
|
|
393
|
-
return 0
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
// One of the items is a pseudo and the other one isn't. Let's move
|
|
397
|
-
// the pseudo to the right.
|
|
398
|
-
if ((a.type === 'pseudo') ^ (z.type === 'pseudo')) {
|
|
399
|
-
return (a.type === 'pseudo') - (z.type === 'pseudo')
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// Both are pseudo's, move the pseudo elements (except for
|
|
403
|
-
// ::file-selector-button) to the right.
|
|
404
|
-
return isPseudoElement(a) - isPseudoElement(z)
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
function isPseudoElement(node) {
|
|
408
|
-
if (node.type !== 'pseudo') return false
|
|
409
|
-
if (pseudoElementExceptions.includes(node.value)) return false
|
|
410
|
-
|
|
411
|
-
return node.value.startsWith('::') || pseudoElementsBC.includes(node.value)
|
|
412
|
-
}
|
|
@@ -297,22 +297,5 @@ export function normalizeConfig(config) {
|
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
-
// Warn if the line-clamp plugin is installed
|
|
301
|
-
if (config.plugins.length > 0) {
|
|
302
|
-
let plugin
|
|
303
|
-
try {
|
|
304
|
-
plugin = require('@tailwindcss/line-clamp')
|
|
305
|
-
} catch {}
|
|
306
|
-
|
|
307
|
-
if (plugin && config.plugins.includes(plugin)) {
|
|
308
|
-
log.warn('line-clamp-in-core', [
|
|
309
|
-
'As of Tailwind CSS v3.3, the `@tailwindcss/line-clamp` plugin is now included by default.',
|
|
310
|
-
'Remove it from the `plugins` array in your configuration to eliminate this warning.',
|
|
311
|
-
])
|
|
312
|
-
|
|
313
|
-
config.plugins = config.plugins.filter((p) => p !== plugin)
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
300
|
return config
|
|
318
301
|
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/** @typedef {import('postcss-selector-parser').Root} Root */
|
|
2
|
+
/** @typedef {import('postcss-selector-parser').Selector} Selector */
|
|
3
|
+
/** @typedef {import('postcss-selector-parser').Pseudo} Pseudo */
|
|
4
|
+
/** @typedef {import('postcss-selector-parser').Node} Node */
|
|
5
|
+
|
|
6
|
+
// There are some pseudo-elements that may or may not be:
|
|
7
|
+
|
|
8
|
+
// **Actionable**
|
|
9
|
+
// Zero or more user-action pseudo-classes may be attached to the pseudo-element itself
|
|
10
|
+
// structural-pseudo-classes are NOT allowed but we don't make
|
|
11
|
+
// The spec is not clear on whether this is allowed or not — but in practice it is.
|
|
12
|
+
|
|
13
|
+
// **Terminal**
|
|
14
|
+
// It MUST be placed at the end of a selector
|
|
15
|
+
//
|
|
16
|
+
// This is the required in the spec. However, some pseudo elements are not "terminal" because
|
|
17
|
+
// they represent a "boundary piercing" that is compiled out by a build step.
|
|
18
|
+
|
|
19
|
+
// **Jumpable**
|
|
20
|
+
// Any terminal element may "jump" over combinators when moving to the end of the selector
|
|
21
|
+
//
|
|
22
|
+
// This is a backwards-compat quirk of :before and :after variants.
|
|
23
|
+
|
|
24
|
+
/** @typedef {'terminal' | 'actionable' | 'jumpable'} PseudoProperty */
|
|
25
|
+
|
|
26
|
+
/** @type {Record<string, PseudoProperty[]>} */
|
|
27
|
+
let elementProperties = {
|
|
28
|
+
'::after': ['terminal', 'jumpable'],
|
|
29
|
+
'::backdrop': ['terminal'],
|
|
30
|
+
'::before': ['terminal', 'jumpable'],
|
|
31
|
+
'::cue': ['terminal'],
|
|
32
|
+
'::cue-region': ['terminal'],
|
|
33
|
+
'::first-letter': ['terminal', 'jumpable'],
|
|
34
|
+
'::first-line': ['terminal', 'jumpable'],
|
|
35
|
+
'::grammar-error': ['terminal'],
|
|
36
|
+
'::marker': ['terminal'],
|
|
37
|
+
'::part': ['terminal', 'actionable'],
|
|
38
|
+
'::placeholder': ['terminal'],
|
|
39
|
+
'::selection': ['terminal'],
|
|
40
|
+
'::slotted': ['terminal'],
|
|
41
|
+
'::spelling-error': ['terminal'],
|
|
42
|
+
'::target-text': ['terminal'],
|
|
43
|
+
|
|
44
|
+
// other
|
|
45
|
+
'::file-selector-button': ['terminal', 'actionable'],
|
|
46
|
+
'::-webkit-progress-bar': ['terminal', 'actionable'],
|
|
47
|
+
|
|
48
|
+
// Webkit scroll bar pseudo elements can be combined with user-action pseudo classes
|
|
49
|
+
'::-webkit-scrollbar': ['terminal', 'actionable'],
|
|
50
|
+
'::-webkit-scrollbar-button': ['terminal', 'actionable'],
|
|
51
|
+
'::-webkit-scrollbar-thumb': ['terminal', 'actionable'],
|
|
52
|
+
'::-webkit-scrollbar-track': ['terminal', 'actionable'],
|
|
53
|
+
'::-webkit-scrollbar-track-piece': ['terminal', 'actionable'],
|
|
54
|
+
'::-webkit-scrollbar-corner': ['terminal', 'actionable'],
|
|
55
|
+
'::-webkit-resizer': ['terminal', 'actionable'],
|
|
56
|
+
|
|
57
|
+
// Note: As a rule, double colons (::) should be used instead of a single colon
|
|
58
|
+
// (:). This distinguishes pseudo-classes from pseudo-elements. However, since
|
|
59
|
+
// this distinction was not present in older versions of the W3C spec, most
|
|
60
|
+
// browsers support both syntaxes for the original pseudo-elements.
|
|
61
|
+
':after': ['terminal', 'jumpable'],
|
|
62
|
+
':before': ['terminal', 'jumpable'],
|
|
63
|
+
':first-letter': ['terminal', 'jumpable'],
|
|
64
|
+
':first-line': ['terminal', 'jumpable'],
|
|
65
|
+
|
|
66
|
+
// The default value is used when the pseudo-element is not recognized
|
|
67
|
+
// Because it's not recognized, we don't know if it's terminal or not
|
|
68
|
+
// So we assume it can't be moved AND can have user-action pseudo classes attached to it
|
|
69
|
+
__default__: ['actionable'],
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {Selector} sel
|
|
74
|
+
* @returns {Selector}
|
|
75
|
+
*/
|
|
76
|
+
export function movePseudos(sel) {
|
|
77
|
+
let [pseudos] = movablePseudos(sel)
|
|
78
|
+
|
|
79
|
+
// Remove all pseudo elements from their respective selectors
|
|
80
|
+
pseudos.forEach(([sel, pseudo]) => sel.removeChild(pseudo))
|
|
81
|
+
|
|
82
|
+
// Re-add them to the end of the selector in the correct order.
|
|
83
|
+
// This moves terminal pseudo elements to the end of the
|
|
84
|
+
// selector otherwise the selector will not be valid.
|
|
85
|
+
//
|
|
86
|
+
// Examples:
|
|
87
|
+
// - `before:hover:text-center` would result in `.before\:hover\:text-center:hover::before`
|
|
88
|
+
// - `hover:before:text-center` would result in `.hover\:before\:text-center:hover::before`
|
|
89
|
+
//
|
|
90
|
+
// The selector `::before:hover` does not work but we
|
|
91
|
+
// can make it work for you by flipping the order.
|
|
92
|
+
sel.nodes.push(...pseudos.map(([, pseudo]) => pseudo))
|
|
93
|
+
|
|
94
|
+
return sel
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** @typedef {[sel: Selector, pseudo: Pseudo, attachedTo: Pseudo | null]} MovablePseudo */
|
|
98
|
+
/** @typedef {[pseudos: MovablePseudo[], lastSeenElement: Pseudo | null]} MovablePseudosResult */
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @param {Selector} sel
|
|
102
|
+
* @returns {MovablePseudosResult}
|
|
103
|
+
*/
|
|
104
|
+
function movablePseudos(sel) {
|
|
105
|
+
/** @type {MovablePseudo[]} */
|
|
106
|
+
let buffer = []
|
|
107
|
+
|
|
108
|
+
/** @type {Pseudo | null} */
|
|
109
|
+
let lastSeenElement = null
|
|
110
|
+
|
|
111
|
+
for (let node of sel.nodes) {
|
|
112
|
+
if (node.type === 'combinator') {
|
|
113
|
+
buffer = buffer.filter(([, node]) => propertiesForPseudo(node).includes('jumpable'))
|
|
114
|
+
lastSeenElement = null
|
|
115
|
+
} else if (node.type === 'pseudo') {
|
|
116
|
+
if (isMovablePseudoElement(node)) {
|
|
117
|
+
lastSeenElement = node
|
|
118
|
+
buffer.push([sel, node, null])
|
|
119
|
+
} else if (lastSeenElement && isAttachablePseudoClass(node, lastSeenElement)) {
|
|
120
|
+
buffer.push([sel, node, lastSeenElement])
|
|
121
|
+
} else {
|
|
122
|
+
lastSeenElement = null
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
for (let sub of node.nodes ?? []) {
|
|
126
|
+
let [movable, lastSeenElementInSub] = movablePseudos(sub)
|
|
127
|
+
lastSeenElement = lastSeenElementInSub || lastSeenElement
|
|
128
|
+
buffer.push(...movable)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return [buffer, lastSeenElement]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @param {Node} node
|
|
138
|
+
* @returns {boolean}
|
|
139
|
+
*/
|
|
140
|
+
function isPseudoElement(node) {
|
|
141
|
+
return node.value.startsWith('::') || elementProperties[node.value] !== undefined
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @param {Node} node
|
|
146
|
+
* @returns {boolean}
|
|
147
|
+
*/
|
|
148
|
+
function isMovablePseudoElement(node) {
|
|
149
|
+
return isPseudoElement(node) && propertiesForPseudo(node).includes('terminal')
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* @param {Node} node
|
|
154
|
+
* @param {Pseudo} pseudo
|
|
155
|
+
* @returns {boolean}
|
|
156
|
+
*/
|
|
157
|
+
function isAttachablePseudoClass(node, pseudo) {
|
|
158
|
+
if (node.type !== 'pseudo') return false
|
|
159
|
+
if (isPseudoElement(node)) return false
|
|
160
|
+
|
|
161
|
+
return propertiesForPseudo(pseudo).includes('actionable')
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @param {Pseudo} pseudo
|
|
166
|
+
* @returns {PseudoProperty[]}
|
|
167
|
+
*/
|
|
168
|
+
function propertiesForPseudo(pseudo) {
|
|
169
|
+
return elementProperties[pseudo.value] ?? elementProperties.__default__
|
|
170
|
+
}
|
|
@@ -9,5 +9,18 @@ export function validateConfig(config) {
|
|
|
9
9
|
])
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
// Warn if the line-clamp plugin is installed
|
|
13
|
+
try {
|
|
14
|
+
let plugin = require('@tailwindcss/line-clamp')
|
|
15
|
+
if (config.plugins.includes(plugin)) {
|
|
16
|
+
log.warn('line-clamp-in-core', [
|
|
17
|
+
'As of Tailwind CSS v3.3, the `@tailwindcss/line-clamp` plugin is now included by default.',
|
|
18
|
+
'Remove it from the `plugins` array in your configuration to eliminate this warning.',
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
config.plugins = config.plugins.filter((p) => p !== plugin)
|
|
22
|
+
}
|
|
23
|
+
} catch {}
|
|
24
|
+
|
|
12
25
|
return config
|
|
13
26
|
}
|
package/stubs/config.full.js
CHANGED
|
@@ -355,6 +355,29 @@ module.exports = {
|
|
|
355
355
|
},
|
|
356
356
|
gap: ({ theme }) => theme('spacing'),
|
|
357
357
|
gradientColorStops: ({ theme }) => theme('colors'),
|
|
358
|
+
gradientColorStopPositions: {
|
|
359
|
+
'0%': '0%',
|
|
360
|
+
'5%': '5%',
|
|
361
|
+
'10%': '10%',
|
|
362
|
+
'15%': '15%',
|
|
363
|
+
'20%': '20%',
|
|
364
|
+
'25%': '25%',
|
|
365
|
+
'30%': '30%',
|
|
366
|
+
'35%': '35%',
|
|
367
|
+
'40%': '40%',
|
|
368
|
+
'45%': '45%',
|
|
369
|
+
'50%': '50%',
|
|
370
|
+
'55%': '55%',
|
|
371
|
+
'60%': '60%',
|
|
372
|
+
'65%': '65%',
|
|
373
|
+
'70%': '70%',
|
|
374
|
+
'75%': '75%',
|
|
375
|
+
'80%': '80%',
|
|
376
|
+
'85%': '85%',
|
|
377
|
+
'90%': '90%',
|
|
378
|
+
'95%': '95%',
|
|
379
|
+
'100%': '100%',
|
|
380
|
+
},
|
|
358
381
|
grayscale: {
|
|
359
382
|
0: '0',
|
|
360
383
|
DEFAULT: '100%',
|
package/types/config.d.ts
CHANGED
|
@@ -46,12 +46,7 @@ type PrefixConfig = string
|
|
|
46
46
|
type SeparatorConfig = string
|
|
47
47
|
|
|
48
48
|
// Safelist related config
|
|
49
|
-
type SafelistConfig =
|
|
50
|
-
| string[]
|
|
51
|
-
| {
|
|
52
|
-
pattern: RegExp
|
|
53
|
-
variants?: string[]
|
|
54
|
-
}[]
|
|
49
|
+
type SafelistConfig = (string | { pattern: RegExp; variants?: string[] })[]
|
|
55
50
|
|
|
56
51
|
// Blocklist related config
|
|
57
52
|
type BlocklistConfig = string[]
|
|
@@ -153,6 +153,30 @@ export type DefaultTheme = Config['theme'] & {
|
|
|
153
153
|
| 'black',
|
|
154
154
|
string
|
|
155
155
|
>
|
|
156
|
+
gradientColorStopPositions: Record<
|
|
157
|
+
| '0%'
|
|
158
|
+
| '5%'
|
|
159
|
+
| '10%'
|
|
160
|
+
| '15%'
|
|
161
|
+
| '20%'
|
|
162
|
+
| '25%'
|
|
163
|
+
| '30%'
|
|
164
|
+
| '35%'
|
|
165
|
+
| '40%'
|
|
166
|
+
| '45%'
|
|
167
|
+
| '50%'
|
|
168
|
+
| '55%'
|
|
169
|
+
| '60%'
|
|
170
|
+
| '65%'
|
|
171
|
+
| '70%'
|
|
172
|
+
| '75%'
|
|
173
|
+
| '80%'
|
|
174
|
+
| '85%'
|
|
175
|
+
| '90%'
|
|
176
|
+
| '95%'
|
|
177
|
+
| '100%',
|
|
178
|
+
string
|
|
179
|
+
>
|
|
156
180
|
grayscale: Record<'0' | 'DEFAULT', string>
|
|
157
181
|
gridAutoColumns: Record<'auto' | 'min' | 'max' | 'fr', string>
|
|
158
182
|
gridAutoRows: Record<'auto' | 'min' | 'max' | 'fr', string>
|