Skip to content

Commit 97062c3

Browse files
adamwathanbradlc
andauthored
Make negative values a first-class feature, rather than theme-driven (#5709)
* WIP * Add failing test for negating default values * Add dynamic negative value opt-in (#5713) * Add `supportsNegativeValues` plugin option * Update `getClassList` to support dynamic negative values * Add test for using a negative scale value with a plugin that does not support dynamic negative values * Support dynamic negation of `DEFAULT` values (#5718) * Add test case Co-authored-by: Brad Cornes <[email protected]>
1 parent b661614 commit 97062c3

13 files changed

+491
-137
lines changed

src/corePlugins.js

+88-57
Original file line numberDiff line numberDiff line change
@@ -529,19 +529,23 @@ export let corePlugins = {
529529
})
530530
},
531531

532-
inset: createUtilityPlugin('inset', [
533-
['inset', ['top', 'right', 'bottom', 'left']],
532+
inset: createUtilityPlugin(
533+
'inset',
534534
[
535-
['inset-x', ['left', 'right']],
536-
['inset-y', ['top', 'bottom']],
537-
],
538-
[
539-
['top', ['top']],
540-
['right', ['right']],
541-
['bottom', ['bottom']],
542-
['left', ['left']],
535+
['inset', ['top', 'right', 'bottom', 'left']],
536+
[
537+
['inset-x', ['left', 'right']],
538+
['inset-y', ['top', 'bottom']],
539+
],
540+
[
541+
['top', ['top']],
542+
['right', ['right']],
543+
['bottom', ['bottom']],
544+
['left', ['left']],
545+
],
543546
],
544-
]),
547+
{ supportsNegativeValues: true }
548+
),
545549

546550
isolation: ({ addUtilities }) => {
547551
addUtilities({
@@ -550,8 +554,8 @@ export let corePlugins = {
550554
})
551555
},
552556

553-
zIndex: createUtilityPlugin('zIndex', [['z', ['zIndex']]]),
554-
order: createUtilityPlugin('order'),
557+
zIndex: createUtilityPlugin('zIndex', [['z', ['zIndex']]], { supportsNegativeValues: true }),
558+
order: createUtilityPlugin('order', undefined, { supportsNegativeValues: true }),
555559
gridColumn: createUtilityPlugin('gridColumn', [['col', ['gridColumn']]]),
556560
gridColumnStart: createUtilityPlugin('gridColumnStart', [['col-start', ['gridColumnStart']]]),
557561
gridColumnEnd: createUtilityPlugin('gridColumnEnd', [['col-end', ['gridColumnEnd']]]),
@@ -576,19 +580,23 @@ export let corePlugins = {
576580
})
577581
},
578582

579-
margin: createUtilityPlugin('margin', [
580-
['m', ['margin']],
581-
[
582-
['mx', ['margin-left', 'margin-right']],
583-
['my', ['margin-top', 'margin-bottom']],
584-
],
583+
margin: createUtilityPlugin(
584+
'margin',
585585
[
586-
['mt', ['margin-top']],
587-
['mr', ['margin-right']],
588-
['mb', ['margin-bottom']],
589-
['ml', ['margin-left']],
586+
['m', ['margin']],
587+
[
588+
['mx', ['margin-left', 'margin-right']],
589+
['my', ['margin-top', 'margin-bottom']],
590+
],
591+
[
592+
['mt', ['margin-top']],
593+
['mr', ['margin-right']],
594+
['mb', ['margin-bottom']],
595+
['ml', ['margin-left']],
596+
],
590597
],
591-
]),
598+
{ supportsNegativeValues: true }
599+
),
592600

593601
boxSizing: ({ addUtilities }) => {
594602
addUtilities({
@@ -653,33 +661,48 @@ export let corePlugins = {
653661
},
654662

655663
transformOrigin: createUtilityPlugin('transformOrigin', [['origin', ['transformOrigin']]]),
656-
translate: createUtilityPlugin('translate', [
664+
translate: createUtilityPlugin(
665+
'translate',
657666
[
658667
[
659-
'translate-x',
660-
[['@defaults transform', {}], '--tw-translate-x', ['transform', 'var(--tw-transform)']],
661-
],
662-
[
663-
'translate-y',
664-
[['@defaults transform', {}], '--tw-translate-y', ['transform', 'var(--tw-transform)']],
668+
[
669+
'translate-x',
670+
[['@defaults transform', {}], '--tw-translate-x', ['transform', 'var(--tw-transform)']],
671+
],
672+
[
673+
'translate-y',
674+
[['@defaults transform', {}], '--tw-translate-y', ['transform', 'var(--tw-transform)']],
675+
],
665676
],
666677
],
667-
]),
668-
rotate: createUtilityPlugin('rotate', [
669-
['rotate', [['@defaults transform', {}], '--tw-rotate', ['transform', 'var(--tw-transform)']]],
670-
]),
671-
skew: createUtilityPlugin('skew', [
678+
{ supportsNegativeValues: true }
679+
),
680+
rotate: createUtilityPlugin(
681+
'rotate',
672682
[
673683
[
674-
'skew-x',
675-
[['@defaults transform', {}], '--tw-skew-x', ['transform', 'var(--tw-transform)']],
684+
'rotate',
685+
[['@defaults transform', {}], '--tw-rotate', ['transform', 'var(--tw-transform)']],
676686
],
687+
],
688+
{ supportsNegativeValues: true }
689+
),
690+
skew: createUtilityPlugin(
691+
'skew',
692+
[
677693
[
678-
'skew-y',
679-
[['@defaults transform', {}], '--tw-skew-y', ['transform', 'var(--tw-transform)']],
694+
[
695+
'skew-x',
696+
[['@defaults transform', {}], '--tw-skew-x', ['transform', 'var(--tw-transform)']],
697+
],
698+
[
699+
'skew-y',
700+
[['@defaults transform', {}], '--tw-skew-y', ['transform', 'var(--tw-transform)']],
701+
],
680702
],
681703
],
682-
]),
704+
{ supportsNegativeValues: true }
705+
),
683706
scale: createUtilityPlugin('scale', [
684707
[
685708
'scale',
@@ -859,19 +882,23 @@ export let corePlugins = {
859882
})
860883
},
861884

862-
scrollMargin: createUtilityPlugin('scrollMargin', [
863-
['scroll-m', ['scroll-margin']],
864-
[
865-
['scroll-mx', ['scroll-margin-left', 'scroll-margin-right']],
866-
['scroll-my', ['scroll-margin-top', 'scroll-margin-bottom']],
867-
],
885+
scrollMargin: createUtilityPlugin(
886+
'scrollMargin',
868887
[
869-
['scroll-mt', ['scroll-margin-top']],
870-
['scroll-mr', ['scroll-margin-right']],
871-
['scroll-mb', ['scroll-margin-bottom']],
872-
['scroll-ml', ['scroll-margin-left']],
888+
['scroll-m', ['scroll-margin']],
889+
[
890+
['scroll-mx', ['scroll-margin-left', 'scroll-margin-right']],
891+
['scroll-my', ['scroll-margin-top', 'scroll-margin-bottom']],
892+
],
893+
[
894+
['scroll-mt', ['scroll-margin-top']],
895+
['scroll-mr', ['scroll-margin-right']],
896+
['scroll-mb', ['scroll-margin-bottom']],
897+
['scroll-ml', ['scroll-margin-left']],
898+
],
873899
],
874-
]),
900+
{ supportsNegativeValues: true }
901+
),
875902

876903
scrollPadding: createUtilityPlugin('scrollPadding', [
877904
['scroll-p', ['scroll-padding']],
@@ -1069,7 +1096,7 @@ export let corePlugins = {
10691096
}
10701097
},
10711098
},
1072-
{ values: theme('space') }
1099+
{ values: theme('space'), supportsNegativeValues: true }
10731100
)
10741101

10751102
addUtilities({
@@ -1641,7 +1668,9 @@ export let corePlugins = {
16411668
})
16421669
},
16431670

1644-
textIndent: createUtilityPlugin('textIndent', [['indent', ['text-indent']]]),
1671+
textIndent: createUtilityPlugin('textIndent', [['indent', ['text-indent']]], {
1672+
supportsNegativeValues: true,
1673+
}),
16451674

16461675
verticalAlign: ({ addUtilities, matchUtilities }) => {
16471676
addUtilities({
@@ -1730,7 +1759,9 @@ export let corePlugins = {
17301759
},
17311760

17321761
lineHeight: createUtilityPlugin('lineHeight', [['leading', ['lineHeight']]]),
1733-
letterSpacing: createUtilityPlugin('letterSpacing', [['tracking', ['letterSpacing']]]),
1762+
letterSpacing: createUtilityPlugin('letterSpacing', [['tracking', ['letterSpacing']]], {
1763+
supportsNegativeValues: true,
1764+
}),
17341765

17351766
textColor: ({ matchUtilities, theme, corePlugins }) => {
17361767
matchUtilities(
@@ -2099,7 +2130,7 @@ export let corePlugins = {
20992130
}
21002131
},
21012132
},
2102-
{ values: theme('hueRotate') }
2133+
{ values: theme('hueRotate'), supportsNegativeValues: true }
21032134
)
21042135
},
21052136

@@ -2250,7 +2281,7 @@ export let corePlugins = {
22502281
}
22512282
},
22522283
},
2253-
{ values: theme('backdropHueRotate') }
2284+
{ values: theme('backdropHueRotate'), supportsNegativeValues: true }
22542285
)
22552286
},
22562287

src/lib/generateRules.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ function* resolveMatchedPlugins(classCandidate, context) {
185185
candidatePrefix = twConfigPrefix + candidatePrefix.slice(twConfigPrefixLen + 1)
186186
}
187187

188+
if (negative && context.candidateRuleMap.has(candidatePrefix)) {
189+
yield [context.candidateRuleMap.get(candidatePrefix), '-DEFAULT']
190+
}
191+
188192
for (let [prefix, modifier] of candidatePermutations(candidatePrefix)) {
189193
if (context.candidateRuleMap.has(prefix)) {
190194
yield [context.candidateRuleMap.get(prefix), negative ? `-${modifier}` : modifier]
@@ -238,7 +242,7 @@ function* resolveMatches(candidate, context) {
238242
}
239243
}
240244
// Only process static plugins on exact matches
241-
else if (modifier === 'DEFAULT') {
245+
else if (modifier === 'DEFAULT' || modifier === '-DEFAULT') {
242246
let ruleSet = plugin
243247
let [rules, options] = parseRules(ruleSet, context.postCssNodeCache)
244248
for (let rule of rules) {

src/lib/setupContextUtils.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import * as sharedState from './sharedState'
1717
import { env } from './sharedState'
1818
import { toPath } from '../util/toPath'
1919
import log from '../util/log'
20+
import negateValue from '../util/negateValue'
2021

2122
function insertInto(list, value, { before = [] } = {}) {
2223
before = [].concat(before)
@@ -300,7 +301,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
300301
function wrapped(modifier, { isOnlyPlugin }) {
301302
let { type = 'any' } = options
302303
type = [].concat(type)
303-
let [value, coercedType] = coerceValue(type, modifier, options.values, tailwindConfig)
304+
let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)
304305

305306
if (value === undefined) {
306307
return []
@@ -352,7 +353,7 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
352353
function wrapped(modifier, { isOnlyPlugin }) {
353354
let { type = 'any' } = options
354355
type = [].concat(type)
355-
let [value, coercedType] = coerceValue(type, modifier, options.values, tailwindConfig)
356+
let [value, coercedType] = coerceValue(type, modifier, options, tailwindConfig)
356357

357358
if (value === undefined) {
358359
return []
@@ -670,10 +671,16 @@ function registerPlugins(plugins, context) {
670671
for (let util of classList) {
671672
if (Array.isArray(util)) {
672673
let [utilName, options] = util
674+
let negativeClasses = []
673675

674-
for (let value of Object.keys(options?.values ?? {})) {
675-
output.push(formatClass(utilName, value))
676+
for (let [key, value] of Object.entries(options?.values ?? {})) {
677+
output.push(formatClass(utilName, key))
678+
if (options?.supportsNegativeValues && negateValue(value)) {
679+
negativeClasses.push(formatClass(utilName, `-${key}`))
680+
}
676681
}
682+
683+
output.push(...negativeClasses)
677684
} else {
678685
output.push(util)
679686
}

src/util/createUtilityPlugin.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import transformThemeValue from './transformThemeValue'
33
export default function createUtilityPlugin(
44
themeKey,
55
utilityVariations = [[themeKey, [themeKey]]],
6-
{ filterDefault = false, type = 'any' } = {}
6+
{ filterDefault = false, ...options } = {}
77
) {
88
let transformValue = transformThemeValue(themeKey)
99
return function ({ matchUtilities, theme }) {
@@ -24,12 +24,12 @@ export default function createUtilityPlugin(
2424
})
2525
}, {}),
2626
{
27+
...options,
2728
values: filterDefault
2829
? Object.fromEntries(
2930
Object.entries(theme(themeKey) ?? {}).filter(([modifier]) => modifier !== 'DEFAULT')
3031
)
3132
: theme(themeKey),
32-
type,
3333
}
3434
)
3535
}

src/util/nameClass.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export function formatClass(classPrefix, key) {
1414
return classPrefix
1515
}
1616

17-
if (key === '-') {
17+
if (key === '-' || key === '-DEFAULT') {
1818
return `-${classPrefix}`
1919
}
2020

src/util/negateValue.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
export default function (value) {
22
value = `${value}`
33

4+
if (value === '0') {
5+
return '0'
6+
}
7+
48
// Flip sign of numbers
59
if (/^[+-]?(\d+|\d*\.\d+)(e[+-]?\d+)?(%|\w+)?$/.test(value)) {
610
return value.replace(/^[+-]?/, (sign) => (sign === '-' ? '' : '-'))
@@ -9,6 +13,4 @@ export default function (value) {
913
if (value.includes('var(') || value.includes('calc(')) {
1014
return `calc(${value} * -1)`
1115
}
12-
13-
return value
1416
}

0 commit comments

Comments
 (0)