tailwindcss 0.0.0-insiders.d6301bd → 0.0.0-insiders.e2d5f21
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/CHANGELOG.md +39 -3
- package/lib/cli.js +60 -60
- package/lib/constants.js +1 -1
- package/lib/corePluginList.js +4 -0
- package/lib/corePlugins.js +157 -54
- package/lib/css/preflight.css +2 -1
- package/lib/featureFlags.js +3 -3
- package/lib/lib/collapseDuplicateDeclarations.js +2 -2
- package/lib/lib/detectNesting.js +17 -2
- package/lib/lib/evaluateTailwindFunctions.js +9 -5
- package/lib/lib/expandApplyAtRules.js +7 -7
- package/lib/lib/expandTailwindAtRules.js +2 -0
- package/lib/lib/generateRules.js +46 -10
- package/lib/lib/resolveDefaultsAtRules.js +2 -2
- package/lib/lib/setupContextUtils.js +15 -72
- package/lib/lib/sharedState.js +1 -1
- package/lib/lib/substituteScreenAtRules.js +7 -4
- package/lib/util/buildMediaQuery.js +13 -24
- package/lib/util/createUtilityPlugin.js +2 -2
- package/lib/util/dataTypes.js +23 -3
- package/lib/util/formatVariantSelector.js +93 -9
- package/lib/util/isValidArbitraryValue.js +64 -0
- package/lib/util/nameClass.js +1 -0
- package/lib/util/normalizeConfig.js +7 -7
- package/lib/util/normalizeScreens.js +58 -0
- package/lib/util/parseBoxShadowValue.js +77 -0
- package/lib/util/pluginUtils.js +12 -11
- package/lib/util/resolveConfig.js +8 -8
- package/package.json +10 -10
- package/peers/index.js +3772 -3072
- package/src/corePluginList.js +1 -1
- package/src/corePlugins.js +143 -61
- package/src/css/preflight.css +2 -1
- package/src/lib/detectNesting.js +22 -3
- package/src/lib/evaluateTailwindFunctions.js +5 -2
- package/src/lib/expandTailwindAtRules.js +2 -0
- package/src/lib/generateRules.js +34 -0
- package/src/lib/setupContextUtils.js +6 -58
- package/src/lib/substituteScreenAtRules.js +6 -3
- package/src/util/buildMediaQuery.js +14 -18
- package/src/util/dataTypes.js +19 -3
- package/src/util/formatVariantSelector.js +92 -1
- package/src/util/isValidArbitraryValue.js +61 -0
- package/src/util/nameClass.js +1 -1
- package/src/util/normalizeScreens.js +42 -0
- package/src/util/parseBoxShadowValue.js +71 -0
- package/src/util/pluginUtils.js +2 -0
- package/stubs/defaultConfig.stub.js +25 -9
package/src/util/dataTypes.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { parseColor } from './color'
|
|
2
|
+
import { parseBoxShadowValue } from './parseBoxShadowValue'
|
|
3
|
+
|
|
4
|
+
let cssFunctions = ['min', 'max', 'clamp', 'calc']
|
|
2
5
|
|
|
3
6
|
// Ref: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Types
|
|
4
7
|
|
|
@@ -50,11 +53,11 @@ export function url(value) {
|
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
export function number(value) {
|
|
53
|
-
return !isNaN(Number(value))
|
|
56
|
+
return !isNaN(Number(value)) || cssFunctions.some((fn) => new RegExp(`^${fn}\\(.+?`).test(value))
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
export function percentage(value) {
|
|
57
|
-
return /%$/g.test(value) ||
|
|
60
|
+
return /%$/g.test(value) || cssFunctions.some((fn) => new RegExp(`^${fn}\\(.+?%`).test(value))
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
let lengthUnits = [
|
|
@@ -78,8 +81,9 @@ let lengthUnits = [
|
|
|
78
81
|
let lengthUnitsPattern = `(?:${lengthUnits.join('|')})`
|
|
79
82
|
export function length(value) {
|
|
80
83
|
return (
|
|
84
|
+
value === '0' ||
|
|
81
85
|
new RegExp(`${lengthUnitsPattern}$`).test(value) ||
|
|
82
|
-
new RegExp(
|
|
86
|
+
cssFunctions.some((fn) => new RegExp(`^${fn}\\(.+?${lengthUnitsPattern}`).test(value))
|
|
83
87
|
)
|
|
84
88
|
}
|
|
85
89
|
|
|
@@ -88,6 +92,18 @@ export function lineWidth(value) {
|
|
|
88
92
|
return lineWidths.has(value)
|
|
89
93
|
}
|
|
90
94
|
|
|
95
|
+
export function shadow(value) {
|
|
96
|
+
let parsedShadows = parseBoxShadowValue(normalize(value))
|
|
97
|
+
|
|
98
|
+
for (let parsedShadow of parsedShadows) {
|
|
99
|
+
if (!parsedShadow.valid) {
|
|
100
|
+
return false
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return true
|
|
105
|
+
}
|
|
106
|
+
|
|
91
107
|
export function color(value) {
|
|
92
108
|
let colors = 0
|
|
93
109
|
|
|
@@ -30,7 +30,17 @@ export function formatVariantSelector(current, ...others) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export function finalizeSelector(format, { selector, candidate, context }) {
|
|
33
|
-
let
|
|
33
|
+
let separator = context?.tailwindConfig?.separator ?? ':'
|
|
34
|
+
|
|
35
|
+
// Split by the separator, but ignore the separator inside square brackets:
|
|
36
|
+
//
|
|
37
|
+
// E.g.: dark:lg:hover:[paint-order:markers]
|
|
38
|
+
// ┬ ┬ ┬ ┬
|
|
39
|
+
// │ │ │ ╰── We will not split here
|
|
40
|
+
// ╰──┴─────┴─────────────── We will split here
|
|
41
|
+
//
|
|
42
|
+
let splitter = new RegExp(`\\${separator}(?![^[]*\\])`)
|
|
43
|
+
let base = candidate.split(splitter).pop()
|
|
34
44
|
|
|
35
45
|
if (context?.tailwindConfig?.prefix) {
|
|
36
46
|
format = prefixSelector(context.tailwindConfig.prefix, format)
|
|
@@ -74,11 +84,92 @@ export function finalizeSelector(format, { selector, candidate, context }) {
|
|
|
74
84
|
return p
|
|
75
85
|
})
|
|
76
86
|
|
|
87
|
+
// This will make sure to move pseudo's to the correct spot (the end for
|
|
88
|
+
// pseudo elements) because otherwise the selector will never work
|
|
89
|
+
// anyway.
|
|
90
|
+
//
|
|
91
|
+
// E.g.:
|
|
92
|
+
// - `before:hover:text-center` would result in `.before\:hover\:text-center:hover::before`
|
|
93
|
+
// - `hover:before:text-center` would result in `.hover\:before\:text-center:hover::before`
|
|
94
|
+
//
|
|
95
|
+
// `::before:hover` doesn't work, which means that we can make it work for you by flipping the order.
|
|
96
|
+
function collectPseudoElements(selector) {
|
|
97
|
+
let nodes = []
|
|
98
|
+
|
|
99
|
+
for (let node of selector.nodes) {
|
|
100
|
+
if (isPseudoElement(node)) {
|
|
101
|
+
nodes.push(node)
|
|
102
|
+
selector.removeChild(node)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (node?.nodes) {
|
|
106
|
+
nodes.push(...collectPseudoElements(node))
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return nodes
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let pseudoElements = collectPseudoElements(selector)
|
|
114
|
+
if (pseudoElements.length > 0) {
|
|
115
|
+
selector.nodes.push(pseudoElements.sort(sortSelector))
|
|
116
|
+
}
|
|
117
|
+
|
|
77
118
|
return selector
|
|
78
119
|
})
|
|
79
120
|
}).processSync(selector)
|
|
80
121
|
}
|
|
81
122
|
|
|
123
|
+
// Note: As a rule, double colons (::) should be used instead of a single colon
|
|
124
|
+
// (:). This distinguishes pseudo-classes from pseudo-elements. However, since
|
|
125
|
+
// this distinction was not present in older versions of the W3C spec, most
|
|
126
|
+
// browsers support both syntaxes for the original pseudo-elements.
|
|
127
|
+
let pseudoElementsBC = [':before', ':after', ':first-line', ':first-letter']
|
|
128
|
+
|
|
129
|
+
// These pseudo-elements _can_ be combined with other pseudo selectors AND the order does matter.
|
|
130
|
+
let pseudoElementExceptions = ['::file-selector-button']
|
|
131
|
+
|
|
132
|
+
// This will make sure to move pseudo's to the correct spot (the end for
|
|
133
|
+
// pseudo elements) because otherwise the selector will never work
|
|
134
|
+
// anyway.
|
|
135
|
+
//
|
|
136
|
+
// E.g.:
|
|
137
|
+
// - `before:hover:text-center` would result in `.before\:hover\:text-center:hover::before`
|
|
138
|
+
// - `hover:before:text-center` would result in `.hover\:before\:text-center:hover::before`
|
|
139
|
+
//
|
|
140
|
+
// `::before:hover` doesn't work, which means that we can make it work
|
|
141
|
+
// for you by flipping the order.
|
|
142
|
+
function sortSelector(a, z) {
|
|
143
|
+
// Both nodes are non-pseudo's so we can safely ignore them and keep
|
|
144
|
+
// them in the same order.
|
|
145
|
+
if (a.type !== 'pseudo' && z.type !== 'pseudo') {
|
|
146
|
+
return 0
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// If one of them is a combinator, we need to keep it in the same order
|
|
150
|
+
// because that means it will start a new "section" in the selector.
|
|
151
|
+
if ((a.type === 'combinator') ^ (z.type === 'combinator')) {
|
|
152
|
+
return 0
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// One of the items is a pseudo and the other one isn't. Let's move
|
|
156
|
+
// the pseudo to the right.
|
|
157
|
+
if ((a.type === 'pseudo') ^ (z.type === 'pseudo')) {
|
|
158
|
+
return (a.type === 'pseudo') - (z.type === 'pseudo')
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Both are pseudo's, move the pseudo elements (except for
|
|
162
|
+
// ::file-selector-button) to the right.
|
|
163
|
+
return isPseudoElement(a) - isPseudoElement(z)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function isPseudoElement(node) {
|
|
167
|
+
if (node.type !== 'pseudo') return false
|
|
168
|
+
if (pseudoElementExceptions.includes(node.value)) return false
|
|
169
|
+
|
|
170
|
+
return node.value.startsWith('::') || pseudoElementsBC.includes(node.value)
|
|
171
|
+
}
|
|
172
|
+
|
|
82
173
|
function resolveFunctionArgument(haystack, needle, arg) {
|
|
83
174
|
let startIdx = haystack.indexOf(arg ? `${needle}(${arg})` : needle)
|
|
84
175
|
if (startIdx === -1) return null
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
let matchingBrackets = new Map([
|
|
2
|
+
['{', '}'],
|
|
3
|
+
['[', ']'],
|
|
4
|
+
['(', ')'],
|
|
5
|
+
])
|
|
6
|
+
let inverseMatchingBrackets = new Map(
|
|
7
|
+
Array.from(matchingBrackets.entries()).map(([k, v]) => [v, k])
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
let quotes = new Set(['"', "'", '`'])
|
|
11
|
+
|
|
12
|
+
// Arbitrary values must contain balanced brackets (), [] and {}. Escaped
|
|
13
|
+
// values don't count, and brackets inside quotes also don't count.
|
|
14
|
+
//
|
|
15
|
+
// E.g.: w-[this-is]w-[weird-and-invalid]
|
|
16
|
+
// E.g.: w-[this-is\\]w-\\[weird-but-valid]
|
|
17
|
+
// E.g.: content-['this-is-also-valid]-weirdly-enough']
|
|
18
|
+
export default function isValidArbitraryValue(value) {
|
|
19
|
+
let stack = []
|
|
20
|
+
let inQuotes = false
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < value.length; i++) {
|
|
23
|
+
let char = value[i]
|
|
24
|
+
|
|
25
|
+
if (char === ':' && !inQuotes && stack.length === 0) {
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Non-escaped quotes allow us to "allow" anything in between
|
|
30
|
+
if (quotes.has(char) && value[i - 1] !== '\\') {
|
|
31
|
+
inQuotes = !inQuotes
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (inQuotes) continue
|
|
35
|
+
if (value[i - 1] === '\\') continue // Escaped
|
|
36
|
+
|
|
37
|
+
if (matchingBrackets.has(char)) {
|
|
38
|
+
stack.push(char)
|
|
39
|
+
} else if (inverseMatchingBrackets.has(char)) {
|
|
40
|
+
let inverse = inverseMatchingBrackets.get(char)
|
|
41
|
+
|
|
42
|
+
// Nothing to pop from, therefore it is unbalanced
|
|
43
|
+
if (stack.length <= 0) {
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Popped value must match the inverse value, otherwise it is unbalanced
|
|
48
|
+
if (stack.pop() !== inverse) {
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// If there is still something on the stack, it is also unbalanced
|
|
55
|
+
if (stack.length > 0) {
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// All good, totally balanced!
|
|
60
|
+
return true
|
|
61
|
+
}
|
package/src/util/nameClass.js
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A function that normalizes the various forms that the screens object can be
|
|
3
|
+
* provided in.
|
|
4
|
+
*
|
|
5
|
+
* Input(s):
|
|
6
|
+
* - ['100px', '200px'] // Raw strings
|
|
7
|
+
* - { sm: '100px', md: '200px' } // Object with string values
|
|
8
|
+
* - { sm: { min: '100px' }, md: { max: '100px' } } // Object with object values
|
|
9
|
+
* - { sm: [{ min: '100px' }, { max: '200px' }] } // Object with object array (multiple values)
|
|
10
|
+
* - [['sm', '100px'], ['md', '200px']] // Tuple object
|
|
11
|
+
*
|
|
12
|
+
* Output(s):
|
|
13
|
+
* - [{ name: 'sm', values: [{ min: '100px', max: '200px' }] }] // List of objects, that contains multiple values
|
|
14
|
+
*/
|
|
15
|
+
export function normalizeScreens(screens) {
|
|
16
|
+
if (Array.isArray(screens)) {
|
|
17
|
+
return screens.map((screen) => {
|
|
18
|
+
if (typeof screen === 'string') {
|
|
19
|
+
return { name: screen.toString(), values: [{ min: screen, max: undefined }] }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let [name, options] = screen
|
|
23
|
+
name = name.toString()
|
|
24
|
+
|
|
25
|
+
if (typeof options === 'string') {
|
|
26
|
+
return { name, values: [{ min: options, max: undefined }] }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (Array.isArray(options)) {
|
|
30
|
+
return { name, values: options.map((option) => resolveValue(option)) }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return { name, values: [resolveValue(options)] }
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return normalizeScreens(Object.entries(screens ?? {}))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function resolveValue({ 'min-width': _minWidth, min = _minWidth, max, raw } = {}) {
|
|
41
|
+
return { min, max, raw }
|
|
42
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
let KEYWORDS = new Set(['inset', 'inherit', 'initial', 'revert', 'unset'])
|
|
2
|
+
let COMMA = /\,(?![^(]*\))/g // Comma separator that is not located between brackets. E.g.: `cubiz-bezier(a, b, c)` these don't count.
|
|
3
|
+
let SPACE = /\ +(?![^(]*\))/g // Similar to the one above, but with spaces instead.
|
|
4
|
+
let LENGTH = /^-?(\d+)(.*?)$/g
|
|
5
|
+
|
|
6
|
+
export function parseBoxShadowValue(input) {
|
|
7
|
+
let shadows = input.split(COMMA)
|
|
8
|
+
return shadows.map((shadow) => {
|
|
9
|
+
let value = shadow.trim()
|
|
10
|
+
let result = { raw: value }
|
|
11
|
+
let parts = value.split(SPACE)
|
|
12
|
+
let seen = new Set()
|
|
13
|
+
|
|
14
|
+
for (let part of parts) {
|
|
15
|
+
// Reset index, since the regex is stateful.
|
|
16
|
+
LENGTH.lastIndex = 0
|
|
17
|
+
|
|
18
|
+
// Keyword
|
|
19
|
+
if (!seen.has('KEYWORD') && KEYWORDS.has(part)) {
|
|
20
|
+
result.keyword = part
|
|
21
|
+
seen.add('KEYWORD')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Length value
|
|
25
|
+
else if (LENGTH.test(part)) {
|
|
26
|
+
if (!seen.has('X')) {
|
|
27
|
+
result.x = part
|
|
28
|
+
seen.add('X')
|
|
29
|
+
} else if (!seen.has('Y')) {
|
|
30
|
+
result.y = part
|
|
31
|
+
seen.add('Y')
|
|
32
|
+
} else if (!seen.has('BLUR')) {
|
|
33
|
+
result.blur = part
|
|
34
|
+
seen.add('BLUR')
|
|
35
|
+
} else if (!seen.has('SPREAD')) {
|
|
36
|
+
result.spread = part
|
|
37
|
+
seen.add('SPREAD')
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Color or unknown
|
|
42
|
+
else {
|
|
43
|
+
if (!result.color) {
|
|
44
|
+
result.color = part
|
|
45
|
+
} else {
|
|
46
|
+
if (!result.unknown) result.unknown = []
|
|
47
|
+
result.unknown.push(part)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check if valid
|
|
53
|
+
result.valid = result.x !== undefined && result.y !== undefined
|
|
54
|
+
|
|
55
|
+
return result
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function formatBoxShadowValue(shadows) {
|
|
60
|
+
return shadows
|
|
61
|
+
.map((shadow) => {
|
|
62
|
+
if (!shadow.valid) {
|
|
63
|
+
return shadow.raw
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return [shadow.keyword, shadow.x, shadow.y, shadow.blur, shadow.spread, shadow.color]
|
|
67
|
+
.filter(Boolean)
|
|
68
|
+
.join(' ')
|
|
69
|
+
})
|
|
70
|
+
.join(', ')
|
|
71
|
+
}
|
package/src/util/pluginUtils.js
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
relativeSize,
|
|
16
16
|
position,
|
|
17
17
|
lineWidth,
|
|
18
|
+
shadow,
|
|
18
19
|
} from './dataTypes'
|
|
19
20
|
import negateValue from './negateValue'
|
|
20
21
|
|
|
@@ -148,6 +149,7 @@ let typeMap = {
|
|
|
148
149
|
'line-width': guess(lineWidth),
|
|
149
150
|
'absolute-size': guess(absoluteSize),
|
|
150
151
|
'relative-size': guess(relativeSize),
|
|
152
|
+
shadow: guess(shadow),
|
|
151
153
|
}
|
|
152
154
|
|
|
153
155
|
let supportedTypes = Object.keys(typeMap)
|
|
@@ -203,14 +203,15 @@ module.exports = {
|
|
|
203
203
|
},
|
|
204
204
|
boxShadow: {
|
|
205
205
|
sm: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
|
|
206
|
-
DEFAULT: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px
|
|
207
|
-
md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -
|
|
208
|
-
lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -
|
|
209
|
-
xl: '0 20px 25px -5px rgb(0 0 0 / 0.1), 0
|
|
206
|
+
DEFAULT: '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
|
|
207
|
+
md: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
|
|
208
|
+
lg: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
|
|
209
|
+
xl: '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',
|
|
210
210
|
'2xl': '0 25px 50px -12px rgb(0 0 0 / 0.25)',
|
|
211
|
-
inner: 'inset 0 2px 4px 0 rgb(0 0 0 / 0.
|
|
211
|
+
inner: 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)',
|
|
212
212
|
none: 'none',
|
|
213
213
|
},
|
|
214
|
+
boxShadowColor: ({ theme }) => theme('colors'),
|
|
214
215
|
caretColor: ({ theme }) => theme('colors'),
|
|
215
216
|
accentColor: ({ theme }) => ({
|
|
216
217
|
...theme('colors'),
|
|
@@ -279,7 +280,7 @@ module.exports = {
|
|
|
279
280
|
'2xl': '0 25px 25px rgb(0 0 0 / 0.15)',
|
|
280
281
|
none: '0 0 #0000',
|
|
281
282
|
},
|
|
282
|
-
fill: {
|
|
283
|
+
fill: ({ theme }) => theme('colors'),
|
|
283
284
|
grayscale: {
|
|
284
285
|
0: '0',
|
|
285
286
|
DEFAULT: '100%',
|
|
@@ -788,9 +789,7 @@ module.exports = {
|
|
|
788
789
|
space: ({ theme }) => ({
|
|
789
790
|
...theme('spacing'),
|
|
790
791
|
}),
|
|
791
|
-
stroke: {
|
|
792
|
-
current: 'currentColor',
|
|
793
|
-
},
|
|
792
|
+
stroke: ({ theme }) => theme('colors'),
|
|
794
793
|
strokeWidth: {
|
|
795
794
|
0: '0',
|
|
796
795
|
1: '1',
|
|
@@ -798,6 +797,23 @@ module.exports = {
|
|
|
798
797
|
},
|
|
799
798
|
textColor: ({ theme }) => theme('colors'),
|
|
800
799
|
textDecorationColor: ({ theme }) => theme('colors'),
|
|
800
|
+
textDecorationThickness: {
|
|
801
|
+
auto: 'auto',
|
|
802
|
+
'from-font': 'from-font',
|
|
803
|
+
0: '0px',
|
|
804
|
+
1: '1px',
|
|
805
|
+
2: '2px',
|
|
806
|
+
4: '4px',
|
|
807
|
+
8: '8px',
|
|
808
|
+
},
|
|
809
|
+
textUnderlineOffset: {
|
|
810
|
+
auto: 'auto',
|
|
811
|
+
0: '0px',
|
|
812
|
+
1: '1px',
|
|
813
|
+
2: '2px',
|
|
814
|
+
4: '4px',
|
|
815
|
+
8: '8px',
|
|
816
|
+
},
|
|
801
817
|
textIndent: ({ theme }) => ({
|
|
802
818
|
...theme('spacing'),
|
|
803
819
|
}),
|