tailwindcss 3.1.8 → 3.2.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.
Files changed (101) hide show
  1. package/CHANGELOG.md +64 -3
  2. package/README.md +6 -5
  3. package/lib/cli/build/deps.js +54 -0
  4. package/lib/cli/build/index.js +44 -0
  5. package/lib/cli/build/plugin.js +351 -0
  6. package/lib/cli/build/utils.js +78 -0
  7. package/lib/cli/build/watching.js +113 -0
  8. package/lib/cli/help/index.js +71 -0
  9. package/lib/cli/index.js +18 -0
  10. package/lib/cli/init/index.js +46 -0
  11. package/lib/cli/shared.js +12 -0
  12. package/lib/cli.js +11 -590
  13. package/lib/corePlugins.js +332 -108
  14. package/lib/css/preflight.css +5 -0
  15. package/lib/featureFlags.js +7 -4
  16. package/lib/index.js +6 -1
  17. package/lib/lib/content.js +167 -0
  18. package/lib/lib/defaultExtractor.js +15 -10
  19. package/lib/lib/detectNesting.js +2 -2
  20. package/lib/lib/evaluateTailwindFunctions.js +17 -1
  21. package/lib/lib/expandApplyAtRules.js +66 -37
  22. package/lib/lib/expandTailwindAtRules.js +10 -42
  23. package/lib/lib/findAtConfigPath.js +44 -0
  24. package/lib/lib/generateRules.js +180 -93
  25. package/lib/lib/normalizeTailwindDirectives.js +1 -1
  26. package/lib/lib/offsets.js +217 -0
  27. package/lib/lib/regex.js +1 -1
  28. package/lib/lib/setupContextUtils.js +339 -100
  29. package/lib/lib/setupTrackingContext.js +5 -39
  30. package/lib/lib/sharedState.js +2 -0
  31. package/lib/public/colors.js +1 -1
  32. package/lib/util/buildMediaQuery.js +6 -3
  33. package/lib/util/configurePlugins.js +1 -1
  34. package/lib/util/dataTypes.js +15 -19
  35. package/lib/util/formatVariantSelector.js +92 -8
  36. package/lib/util/getAllConfigs.js +14 -3
  37. package/lib/util/isValidArbitraryValue.js +1 -1
  38. package/lib/util/nameClass.js +3 -0
  39. package/lib/util/negateValue.js +15 -2
  40. package/lib/util/normalizeConfig.js +17 -3
  41. package/lib/util/normalizeScreens.js +100 -3
  42. package/lib/util/parseAnimationValue.js +1 -1
  43. package/lib/util/parseBoxShadowValue.js +1 -1
  44. package/lib/util/parseDependency.js +33 -54
  45. package/lib/util/parseGlob.js +34 -0
  46. package/lib/util/parseObjectStyles.js +1 -1
  47. package/lib/util/pluginUtils.js +87 -17
  48. package/lib/util/resolveConfig.js +2 -2
  49. package/lib/util/splitAtTopLevelOnly.js +31 -81
  50. package/lib/util/transformThemeValue.js +9 -2
  51. package/lib/util/validateConfig.js +1 -1
  52. package/lib/util/validateFormalSyntax.js +24 -0
  53. package/package.json +14 -13
  54. package/peers/index.js +3263 -1887
  55. package/plugin.d.ts +3 -3
  56. package/scripts/release-channel.js +18 -0
  57. package/scripts/release-notes.js +21 -0
  58. package/src/cli/build/deps.js +56 -0
  59. package/src/cli/build/index.js +45 -0
  60. package/src/cli/build/plugin.js +417 -0
  61. package/src/cli/build/utils.js +76 -0
  62. package/src/cli/build/watching.js +134 -0
  63. package/src/cli/help/index.js +70 -0
  64. package/src/cli/index.js +3 -0
  65. package/src/cli/init/index.js +50 -0
  66. package/src/cli/shared.js +5 -0
  67. package/src/cli.js +4 -696
  68. package/src/corePlugins.js +262 -39
  69. package/src/css/preflight.css +5 -0
  70. package/src/featureFlags.js +12 -2
  71. package/src/index.js +5 -0
  72. package/src/lib/content.js +205 -0
  73. package/src/lib/defaultExtractor.js +3 -0
  74. package/src/lib/evaluateTailwindFunctions.js +22 -1
  75. package/src/lib/expandApplyAtRules.js +70 -29
  76. package/src/lib/expandTailwindAtRules.js +8 -46
  77. package/src/lib/findAtConfigPath.js +48 -0
  78. package/src/lib/generateRules.js +223 -101
  79. package/src/lib/offsets.js +270 -0
  80. package/src/lib/setupContextUtils.js +376 -89
  81. package/src/lib/setupTrackingContext.js +4 -45
  82. package/src/lib/sharedState.js +2 -0
  83. package/src/util/buildMediaQuery.js +5 -3
  84. package/src/util/dataTypes.js +15 -17
  85. package/src/util/formatVariantSelector.js +113 -9
  86. package/src/util/getAllConfigs.js +14 -2
  87. package/src/util/nameClass.js +4 -0
  88. package/src/util/negateValue.js +10 -2
  89. package/src/util/normalizeConfig.js +22 -2
  90. package/src/util/normalizeScreens.js +99 -4
  91. package/src/util/parseBoxShadowValue.js +1 -1
  92. package/src/util/parseDependency.js +37 -42
  93. package/src/util/parseGlob.js +24 -0
  94. package/src/util/pluginUtils.js +96 -14
  95. package/src/util/resolveConfig.js +1 -1
  96. package/src/util/splitAtTopLevelOnly.js +23 -49
  97. package/src/util/transformThemeValue.js +9 -1
  98. package/src/util/validateFormalSyntax.js +34 -0
  99. package/stubs/defaultConfig.stub.js +20 -3
  100. package/types/config.d.ts +48 -13
  101. package/types/generated/default-theme.d.ts +11 -0
@@ -0,0 +1,24 @@
1
+ import globParent from 'glob-parent'
2
+
3
+ // Based on `glob-base`
4
+ // https://github.com/micromatch/glob-base/blob/master/index.js
5
+ export function parseGlob(pattern) {
6
+ let glob = pattern
7
+ let base = globParent(pattern)
8
+
9
+ if (base !== '.') {
10
+ glob = pattern.substr(base.length)
11
+ if (glob.charAt(0) === '/') {
12
+ glob = glob.substr(1)
13
+ }
14
+ }
15
+
16
+ if (glob.substr(0, 2) === './') {
17
+ glob = glob.substr(2)
18
+ }
19
+ if (glob.charAt(0) === '/') {
20
+ glob = glob.substr(1)
21
+ }
22
+
23
+ return { base, glob }
24
+ }
@@ -18,6 +18,8 @@ import {
18
18
  shadow,
19
19
  } from './dataTypes'
20
20
  import negateValue from './negateValue'
21
+ import { backgroundSize } from './validateFormalSyntax'
22
+ import { flagEnabled } from '../featureFlags.js'
21
23
 
22
24
  export function updateAllClasses(selectors, updateClass) {
23
25
  let parser = selectorParser((selectors) => {
@@ -85,11 +87,20 @@ function isArbitraryValue(input) {
85
87
  return input.startsWith('[') && input.endsWith(']')
86
88
  }
87
89
 
88
- function splitAlpha(modifier) {
90
+ function splitUtilityModifier(modifier) {
89
91
  let slashIdx = modifier.lastIndexOf('/')
90
92
 
91
93
  if (slashIdx === -1 || slashIdx === modifier.length - 1) {
92
- return [modifier]
94
+ return [modifier, undefined]
95
+ }
96
+
97
+ let arbitrary = isArbitraryValue(modifier)
98
+
99
+ // The modifier could be of the form `[foo]/[bar]`
100
+ // We want to handle this case properly
101
+ // without affecting `[foo/bar]`
102
+ if (arbitrary && !modifier.includes(']/[')) {
103
+ return [modifier, undefined]
93
104
  }
94
105
 
95
106
  return [modifier.slice(0, slashIdx), modifier.slice(slashIdx + 1)]
@@ -105,12 +116,18 @@ export function parseColorFormat(value) {
105
116
  return value
106
117
  }
107
118
 
108
- export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) {
109
- if (options.values?.[modifier] !== undefined) {
110
- return parseColorFormat(options.values?.[modifier])
119
+ export function asColor(
120
+ _,
121
+ options = {},
122
+ { tailwindConfig = {}, utilityModifier, rawModifier } = {}
123
+ ) {
124
+ if (options.values?.[rawModifier] !== undefined) {
125
+ return parseColorFormat(options.values?.[rawModifier])
111
126
  }
112
127
 
113
- let [color, alpha] = splitAlpha(modifier)
128
+ // TODO: Hoist this up to getMatchingTypes or something
129
+ // We do this here because we need the alpha value (if any)
130
+ let [color, alpha] = splitUtilityModifier(rawModifier)
114
131
 
115
132
  if (alpha !== undefined) {
116
133
  let normalizedColor =
@@ -133,7 +150,7 @@ export function asColor(modifier, options = {}, { tailwindConfig = {} } = {}) {
133
150
  return withAlphaValue(normalizedColor, tailwindConfig.theme.opacity[alpha])
134
151
  }
135
152
 
136
- return asValue(modifier, options, { validate: validateColor })
153
+ return asValue(rawModifier, options, { rawModifier, utilityModifier, validate: validateColor })
137
154
  }
138
155
 
139
156
  export function asLookupValue(modifier, options = {}) {
@@ -141,12 +158,12 @@ export function asLookupValue(modifier, options = {}) {
141
158
  }
142
159
 
143
160
  function guess(validate) {
144
- return (modifier, options) => {
145
- return asValue(modifier, options, { validate })
161
+ return (modifier, options, extras) => {
162
+ return asValue(modifier, options, { ...extras, validate })
146
163
  }
147
164
  }
148
165
 
149
- let typeMap = {
166
+ export let typeMap = {
150
167
  any: asValue,
151
168
  color: asColor,
152
169
  url: guess(url),
@@ -162,6 +179,7 @@ let typeMap = {
162
179
  'absolute-size': guess(absoluteSize),
163
180
  'relative-size': guess(relativeSize),
164
181
  shadow: guess(shadow),
182
+ size: guess(backgroundSize),
165
183
  }
166
184
 
167
185
  let supportedTypes = Object.keys(typeMap)
@@ -190,15 +208,79 @@ export function coerceValue(types, modifier, options, tailwindConfig) {
190
208
  }
191
209
 
192
210
  if (value.length > 0 && supportedTypes.includes(explicitType)) {
193
- return [asValue(`[${value}]`, options), explicitType]
211
+ return [asValue(`[${value}]`, options), explicitType, null]
194
212
  }
195
213
  }
196
214
 
215
+ let matches = getMatchingTypes(types, modifier, options, tailwindConfig)
216
+
197
217
  // Find first matching type
198
- for (let type of [].concat(types)) {
199
- let result = typeMap[type](modifier, options, { tailwindConfig })
200
- if (result !== undefined) return [result, type]
218
+ for (let match of matches) {
219
+ return match
201
220
  }
202
221
 
203
222
  return []
204
223
  }
224
+
225
+ /**
226
+ *
227
+ * @param {{type: string}[]} types
228
+ * @param {string} rawModifier
229
+ * @param {any} options
230
+ * @param {any} tailwindConfig
231
+ * @returns {Iterator<[value: string, type: string, modifier: string | null]>}
232
+ */
233
+ export function* getMatchingTypes(types, rawModifier, options, tailwindConfig) {
234
+ let modifiersEnabled = flagEnabled(tailwindConfig, 'generalizedModifiers')
235
+
236
+ let [modifier, utilityModifier] = splitUtilityModifier(rawModifier)
237
+
238
+ let canUseUtilityModifier =
239
+ modifiersEnabled &&
240
+ options.modifiers != null &&
241
+ (options.modifiers === 'any' ||
242
+ (typeof options.modifiers === 'object' &&
243
+ ((utilityModifier && isArbitraryValue(utilityModifier)) ||
244
+ utilityModifier in options.modifiers)))
245
+
246
+ if (!canUseUtilityModifier) {
247
+ modifier = rawModifier
248
+ utilityModifier = undefined
249
+ }
250
+
251
+ if (utilityModifier !== undefined && modifier === '') {
252
+ modifier = 'DEFAULT'
253
+ }
254
+
255
+ // Check the full value first
256
+ // TODO: Move to asValue… somehow
257
+ if (utilityModifier !== undefined) {
258
+ if (typeof options.modifiers === 'object') {
259
+ let configValue = options.modifiers?.[utilityModifier] ?? null
260
+ if (configValue !== null) {
261
+ utilityModifier = configValue
262
+ } else if (isArbitraryValue(utilityModifier)) {
263
+ utilityModifier = utilityModifier.slice(1, -1)
264
+ }
265
+ }
266
+
267
+ let result = asValue(rawModifier, options, { rawModifier, utilityModifier, tailwindConfig })
268
+ if (result !== undefined) {
269
+ yield [result, 'any', null]
270
+ }
271
+ }
272
+
273
+ for (const { type } of types ?? []) {
274
+ let result = typeMap[type](modifier, options, {
275
+ rawModifier,
276
+ utilityModifier,
277
+ tailwindConfig,
278
+ })
279
+
280
+ if (result === undefined) {
281
+ continue
282
+ }
283
+
284
+ yield [result, type, utilityModifier ?? null]
285
+ }
286
+ }
@@ -29,7 +29,7 @@ function mergeWith(target, ...sources) {
29
29
 
30
30
  if (merged === undefined) {
31
31
  if (isObject(target[k]) && isObject(source[k])) {
32
- target[k] = mergeWith(target[k], source[k], customizer)
32
+ target[k] = mergeWith({}, target[k], source[k], customizer)
33
33
  } else {
34
34
  target[k] = source[k]
35
35
  }
@@ -1,5 +1,3 @@
1
- import * as regex from '../lib/regex'
2
-
3
1
  /**
4
2
  * This splits a string on a top-level character.
5
3
  *
@@ -15,57 +13,33 @@ import * as regex from '../lib/regex'
15
13
  * @param {string} input
16
14
  * @param {string} separator
17
15
  */
18
- export function* splitAtTopLevelOnly(input, separator) {
19
- let SPECIALS = new RegExp(`[(){}\\[\\]${regex.escape(separator)}]`, 'g')
20
-
21
- let depth = 0
22
- let lastIndex = 0
23
- let found = false
24
- let separatorIndex = 0
25
- let separatorStart = 0
26
- let separatorLength = separator.length
27
-
28
- // Find all paren-like things & character
29
- // And only split on commas if they're top-level
30
- for (let match of input.matchAll(SPECIALS)) {
31
- let matchesSeparator = match[0] === separator[separatorIndex]
32
- let atEndOfSeparator = separatorIndex === separatorLength - 1
33
- let matchesFullSeparator = matchesSeparator && atEndOfSeparator
34
-
35
- if (match[0] === '(') depth++
36
- if (match[0] === ')') depth--
37
- if (match[0] === '[') depth++
38
- if (match[0] === ']') depth--
39
- if (match[0] === '{') depth++
40
- if (match[0] === '}') depth--
41
-
42
- if (matchesSeparator && depth === 0) {
43
- if (separatorStart === 0) {
44
- separatorStart = match.index
16
+ export function splitAtTopLevelOnly(input, separator) {
17
+ let stack = []
18
+ let parts = []
19
+ let lastPos = 0
20
+
21
+ for (let idx = 0; idx < input.length; idx++) {
22
+ let char = input[idx]
23
+
24
+ if (stack.length === 0 && char === separator[0]) {
25
+ if (separator.length === 1 || input.slice(idx, idx + separator.length) === separator) {
26
+ parts.push(input.slice(lastPos, idx))
27
+ lastPos = idx + separator.length
45
28
  }
46
-
47
- separatorIndex++
48
29
  }
49
30
 
50
- if (matchesFullSeparator && depth === 0) {
51
- found = true
52
-
53
- yield input.substring(lastIndex, separatorStart)
54
- lastIndex = separatorStart + separatorLength
55
- }
56
-
57
- if (separatorIndex === separatorLength) {
58
- separatorIndex = 0
59
- separatorStart = 0
31
+ if (char === '(' || char === '[' || char === '{') {
32
+ stack.push(char)
33
+ } else if (
34
+ (char === ')' && stack[stack.length - 1] === '(') ||
35
+ (char === ']' && stack[stack.length - 1] === '[') ||
36
+ (char === '}' && stack[stack.length - 1] === '{')
37
+ ) {
38
+ stack.pop()
60
39
  }
61
40
  }
62
41
 
63
- // Provide the last segment of the string if available
64
- // Otherwise the whole string since no `char`s were found
65
- // This mirrors the behavior of string.split()
66
- if (found) {
67
- yield input.substring(lastIndex)
68
- } else {
69
- yield input
70
- }
42
+ parts.push(input.slice(lastPos))
43
+
44
+ return parts
71
45
  }
@@ -1,4 +1,5 @@
1
1
  import postcss from 'postcss'
2
+ import isPlainObject from './isPlainObject'
2
3
 
3
4
  export default function transformThemeValue(themeSection) {
4
5
  if (['fontSize', 'outline'].includes(themeSection)) {
@@ -10,9 +11,16 @@ export default function transformThemeValue(themeSection) {
10
11
  }
11
12
  }
12
13
 
14
+ if (themeSection === 'fontFamily') {
15
+ return (value) => {
16
+ if (typeof value === 'function') value = value({})
17
+ let families = Array.isArray(value) && isPlainObject(value[1]) ? value[0] : value
18
+ return Array.isArray(families) ? families.join(', ') : families
19
+ }
20
+ }
21
+
13
22
  if (
14
23
  [
15
- 'fontFamily',
16
24
  'boxShadow',
17
25
  'transitionProperty',
18
26
  'transitionDuration',
@@ -0,0 +1,34 @@
1
+ import { length, percentage } from './dataTypes'
2
+ import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
3
+
4
+ /**
5
+ *
6
+ * https://developer.mozilla.org/en-US/docs/Web/CSS/background-size#formal_syntax
7
+ *
8
+ * background-size =
9
+ * <bg-size>#
10
+ *
11
+ * <bg-size> =
12
+ * [ <length-percentage [0,∞]> | auto ]{1,2} |
13
+ * cover |
14
+ * contain
15
+ *
16
+ * <length-percentage> =
17
+ * <length> |
18
+ * <percentage>
19
+ *
20
+ * @param {string} value
21
+ */
22
+ export function backgroundSize(value) {
23
+ let keywordValues = ['cover', 'contain']
24
+ // the <length-percentage> type will probably be a css function
25
+ // so we have to use `splitAtTopLevelOnly`
26
+ return splitAtTopLevelOnly(value, ',').every((part) => {
27
+ let sizes = splitAtTopLevelOnly(part, '_').filter(Boolean)
28
+ if (sizes.length === 1 && keywordValues.includes(sizes[0])) return true
29
+
30
+ if (sizes.length !== 1 && sizes.length !== 2) return false
31
+
32
+ return sizes.every((size) => length(size) || percentage(size) || size === 'auto')
33
+ })
34
+ }
@@ -11,6 +11,7 @@ module.exports = {
11
11
  xl: '1280px',
12
12
  '2xl': '1536px',
13
13
  },
14
+ supports: {},
14
15
  colors: ({ colors }) => ({
15
16
  inherit: colors.inherit,
16
17
  current: colors.current,
@@ -112,6 +113,16 @@ module.exports = {
112
113
  pulse: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
113
114
  bounce: 'bounce 1s infinite',
114
115
  },
116
+ aria: {
117
+ checked: 'checked="true"',
118
+ disabled: 'disabled="true"',
119
+ expanded: 'expanded="true"',
120
+ hidden: 'hidden="true"',
121
+ pressed: 'pressed="true"',
122
+ readonly: 'readonly="true"',
123
+ required: 'required="true"',
124
+ selected: 'selected="true"',
125
+ },
115
126
  aspectRatio: {
116
127
  auto: 'auto',
117
128
  square: '1 / 1',
@@ -284,7 +295,10 @@ module.exports = {
284
295
  '2xl': '0 25px 25px rgb(0 0 0 / 0.15)',
285
296
  none: '0 0 #0000',
286
297
  },
287
- fill: ({ theme }) => theme('colors'),
298
+ fill: ({ theme }) => ({
299
+ none: 'none',
300
+ ...theme('colors'),
301
+ }),
288
302
  grayscale: {
289
303
  0: '0',
290
304
  DEFAULT: '100%',
@@ -721,7 +735,7 @@ module.exports = {
721
735
  8: '8px',
722
736
  },
723
737
  ringColor: ({ theme }) => ({
724
- DEFAULT: theme(`colors.blue.500`, '#3b82f6'),
738
+ DEFAULT: theme('colors.blue.500', '#3b82f6'),
725
739
  ...theme('colors'),
726
740
  }),
727
741
  ringOffsetColor: ({ theme }) => theme('colors'),
@@ -793,7 +807,10 @@ module.exports = {
793
807
  space: ({ theme }) => ({
794
808
  ...theme('spacing'),
795
809
  }),
796
- stroke: ({ theme }) => theme('colors'),
810
+ stroke: ({ theme }) => ({
811
+ none: 'none',
812
+ ...theme('colors'),
813
+ }),
797
814
  strokeWidth: {
798
815
  0: '0',
799
816
  1: '1',
package/types/config.d.ts CHANGED
@@ -12,7 +12,7 @@ interface RecursiveKeyValuePair<K extends keyof any = string, V = string> {
12
12
  [key: string]: V | RecursiveKeyValuePair<K, V>
13
13
  }
14
14
  type ResolvableTo<T> = T | ((utils: PluginUtils) => T)
15
- type CSSRuleObject = RecursiveKeyValuePair<string, string | string[]>
15
+ type CSSRuleObject = RecursiveKeyValuePair<string, null | string | string[]>
16
16
 
17
17
  interface PluginUtils {
18
18
  colors: DefaultColors
@@ -31,6 +31,7 @@ type ContentConfig =
31
31
  | (FilePath | RawFile)[]
32
32
  | {
33
33
  files: (FilePath | RawFile)[]
34
+ relative?: boolean
34
35
  extract?: ExtractorFn | { [extension: string]: ExtractorFn }
35
36
  transform?: TransformerFn | { [extension: string]: TransformerFn }
36
37
  }
@@ -49,27 +50,31 @@ type SafelistConfig =
49
50
  | string[]
50
51
  | {
51
52
  pattern: RegExp
52
- variants: string[]
53
+ variants?: string[]
53
54
  }[]
54
55
 
55
56
  // Presets related config
56
57
  type PresetsConfig = Config[]
57
58
 
58
59
  // Future related config
59
- type FutureConfigValues = never // Replace with 'future-feature-1' | 'future-feature-2'
60
+ type FutureConfigValues =
61
+ | 'hoverOnlyWhenSupported'
62
+ | 'respectDefaultRingColorOpacity'
63
+ | 'disableColorOpacityUtilitiesByDefault'
64
+ | 'relativeContentPathsByDefault'
60
65
  type FutureConfig = Expand<'all' | Partial<Record<FutureConfigValues, boolean>>> | []
61
66
 
62
67
  // Experimental related config
63
- type ExperimentalConfigValues = 'optimizeUniversalDefaults' // Replace with 'experimental-feature-1' | 'experimental-feature-2'
68
+ type ExperimentalConfigValues = 'optimizeUniversalDefaults' | 'matchVariant'
64
69
  type ExperimentalConfig = Expand<'all' | Partial<Record<ExperimentalConfigValues, boolean>>> | []
65
70
 
66
71
  // DarkMode related config
67
72
  type DarkModeConfig =
68
73
  // Use the `media` query strategy.
69
74
  | 'media'
70
- // Use the `class` stategy, which requires a `.dark` class on the `html`.
75
+ // Use the `class` strategy, which requires a `.dark` class on the `html`.
71
76
  | 'class'
72
- // Use the `class` stategy with a custom class instead of `.dark`.
77
+ // Use the `class` strategy with a custom class instead of `.dark`.
73
78
  | ['class', string]
74
79
 
75
80
  type Screen = { raw: string } | { min: string } | { max: string } | { min: string; max: string }
@@ -79,6 +84,7 @@ type ScreensConfig = string[] | KeyValuePair<string, string | Screen | Screen[]>
79
84
  interface ThemeConfig {
80
85
  // Responsiveness
81
86
  screens: ResolvableTo<ScreensConfig>
87
+ supports: ResolvableTo<Record<string, string>>
82
88
 
83
89
  // Reusable base configs
84
90
  colors: ResolvableTo<RecursiveKeyValuePair>
@@ -153,7 +159,14 @@ interface ThemeConfig {
153
159
  objectPosition: ResolvableTo<KeyValuePair>
154
160
  padding: ThemeConfig['spacing']
155
161
  textIndent: ThemeConfig['spacing']
156
- fontFamily: ResolvableTo<KeyValuePair<string, string[]>>
162
+ fontFamily: ResolvableTo<
163
+ KeyValuePair<
164
+ string,
165
+ | string
166
+ | string[]
167
+ | [fontFamily: string | string[], configuration: Partial<{ fontFeatureSettings: string }>]
168
+ >
169
+ >
157
170
  fontSize: ResolvableTo<
158
171
  KeyValuePair<
159
172
  string,
@@ -251,13 +264,17 @@ export interface PluginAPI {
251
264
  }>
252
265
  ): void
253
266
  // for registering new dynamic utility styles
254
- matchUtilities<T>(
255
- utilities: KeyValuePair<string, (value: T) => CSSRuleObject>,
267
+ matchUtilities<T = string, U = string>(
268
+ utilities: KeyValuePair<
269
+ string,
270
+ (value: T | string, extra: { modifier: U | string | null }) => CSSRuleObject | null
271
+ >,
256
272
  options?: Partial<{
257
273
  respectPrefix: boolean
258
274
  respectImportant: boolean
259
275
  type: ValueType | ValueType[]
260
276
  values: KeyValuePair<string, T>
277
+ modifiers: 'any' | KeyValuePair<string, U>
261
278
  supportsNegativeValues: boolean
262
279
  }>
263
280
  ): void
@@ -270,13 +287,17 @@ export interface PluginAPI {
270
287
  }>
271
288
  ): void
272
289
  // for registering new dynamic component styles
273
- matchComponents<T>(
274
- components: KeyValuePair<string, (value: T) => CSSRuleObject>,
290
+ matchComponents<T = string, U = string>(
291
+ components: KeyValuePair<
292
+ string,
293
+ (value: T | string, extra: { modifier: U | string | null }) => CSSRuleObject | null
294
+ >,
275
295
  options?: Partial<{
276
296
  respectPrefix: boolean
277
297
  respectImportant: boolean
278
298
  type: ValueType | ValueType[]
279
299
  values: KeyValuePair<string, T>
300
+ modifiers: 'any' | KeyValuePair<string, U>
280
301
  supportsNegativeValues: boolean
281
302
  }>
282
303
  ): void
@@ -284,6 +305,17 @@ export interface PluginAPI {
284
305
  addBase(base: CSSRuleObject | CSSRuleObject[]): void
285
306
  // for registering custom variants
286
307
  addVariant(name: string, definition: string | string[] | (() => string) | (() => string)[]): void
308
+ matchVariant<T = string>(
309
+ name: string,
310
+ cb: (value: T | string, extra: { modifier: string | null }) => string | string[],
311
+ options?: {
312
+ values?: KeyValuePair<string, T>
313
+ sort?(
314
+ a: { value: T | string; modifier: string | null },
315
+ b: { value: T | string; modifier: string | null }
316
+ ): number
317
+ }
318
+ ): void
287
319
  // for looking up values in the user’s theme configuration
288
320
  theme: <TDefaultValue = Config['theme']>(
289
321
  path?: string,
@@ -299,8 +331,11 @@ export interface PluginAPI {
299
331
  export type PluginCreator = (api: PluginAPI) => void
300
332
  export type PluginsConfig = (
301
333
  | PluginCreator
302
- | { handler: PluginCreator; config?: Config }
303
- | { (options: any): { handler: PluginCreator; config?: Config }; __isOptionsFunction: true }
334
+ | { handler: PluginCreator; config?: Partial<Config> }
335
+ | {
336
+ (options: any): { handler: PluginCreator; config?: Partial<Config> }
337
+ __isOptionsFunction: true
338
+ }
304
339
  )[]
305
340
 
306
341
  // Top level config related
@@ -70,6 +70,17 @@ export type DefaultTheme = Config['theme'] & {
70
70
  string
71
71
  >
72
72
  animation: Record<'none' | 'spin' | 'ping' | 'pulse' | 'bounce', string>
73
+ aria: Record<
74
+ | 'checked'
75
+ | 'disabled'
76
+ | 'expanded'
77
+ | 'hidden'
78
+ | 'pressed'
79
+ | 'readonly'
80
+ | 'required'
81
+ | 'selected',
82
+ string
83
+ >
73
84
  aspectRatio: Record<'auto' | 'square' | 'video', string>
74
85
  backgroundImage: Record<
75
86
  | 'none'