Improve compatibility with special default values in JS configs (#19348)
Fixes #19345 In v3 the `ringColor.DEFAULT` option was used as the default color for ring utilities (when it was defined). This currently gets translated as `--ring-color` but that doesn't work this way in v4. Instead it should translate to `--default-ring-color` and *not* `--ring-color`. I've also tweaked the upgrade tool to handle this properly as well.
This commit is contained in:
parent
af481175e7
commit
764275143e
7 changed files with 97 additions and 27 deletions
|
|
@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Support environment API in `@tailwindcss/vite` ([#18970](https://github.com/tailwindlabs/tailwindcss/pull/18970))
|
||||
- Preserve case of theme keys from JS configs and plugins ([#19337](https://github.com/tailwindlabs/tailwindcss/pull/19337))
|
||||
- Write source maps correctly on the CLI when using `--watch` ([#19373](https://github.com/tailwindlabs/tailwindcss/pull/19373))
|
||||
- Handle special defaults (like `ringColor.DEFAULT`) in JS configs ([#19348](https://github.com/tailwindlabs/tailwindcss/pull/19348))
|
||||
- Upgrade: Handle `future` and `experimental` config keys ([#19344](https://github.com/tailwindlabs/tailwindcss/pull/19344))
|
||||
- Try to canonicalize any arbitrary utility to a bare value ([#19379](https://github.com/tailwindlabs/tailwindcss/pull/19379))
|
||||
|
||||
|
|
|
|||
|
|
@ -2347,8 +2347,7 @@ test(
|
|||
--shadow-*: initial;
|
||||
--shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
|
||||
--ring-width-*: initial;
|
||||
--ring-width: 4px;
|
||||
--default-ring-width: 4px;
|
||||
|
||||
--blur: var(--custom-default-blur);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ test(
|
|||
steel: 'rgb(70 130 180 / <alpha-value>)',
|
||||
smoke: 'rgba(245, 245, 245, var(--smoke-alpha, <alpha-value>))',
|
||||
},
|
||||
ringColor: {
|
||||
DEFAULT: '#c0ffee',
|
||||
},
|
||||
opacity: {
|
||||
superOpaque: '0.95',
|
||||
},
|
||||
|
|
@ -191,6 +194,8 @@ test(
|
|||
--color-steel: rgb(70 130 180);
|
||||
--color-smoke: rgba(245, 245, 245, var(--smoke-alpha, 1));
|
||||
|
||||
--default-ring-color: #c0ffee;
|
||||
|
||||
--opacity-*: initial;
|
||||
--opacity-superOpaque: 95%;
|
||||
|
||||
|
|
|
|||
|
|
@ -237,16 +237,21 @@ async function migrateTheme(
|
|||
prevSectionKey = sectionKey
|
||||
}
|
||||
|
||||
if (resetNamespaces.has(key[0]) && resetNamespaces.get(key[0]) === false) {
|
||||
resetNamespaces.set(key[0], true)
|
||||
let property = keyPathToCssProperty([key[0]])
|
||||
if (property !== null) {
|
||||
themeSection.push(` ${escape(`--${property}`)}-*: initial;`)
|
||||
}
|
||||
}
|
||||
|
||||
let property = keyPathToCssProperty(key)
|
||||
|
||||
if (property !== null) {
|
||||
if (
|
||||
!property.startsWith('default-') &&
|
||||
resetNamespaces.has(key[0]) &&
|
||||
resetNamespaces.get(key[0]) === false
|
||||
) {
|
||||
resetNamespaces.set(key[0], true)
|
||||
let ns = keyPathToCssProperty([key[0]])
|
||||
if (ns !== null) {
|
||||
themeSection.push(` ${escape(`--${ns}`)}-*: initial;`)
|
||||
}
|
||||
}
|
||||
|
||||
themeSection.push(` ${escape(`--${property}`)}: ${value};`)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ const THEME_KEYS = new Map([
|
|||
['backdrop-blur-sm', '--backdrop-blur-sm'],
|
||||
['backdrop-blur-xs', '--backdrop-blur-xs'],
|
||||
|
||||
['ring', '--ring-width'],
|
||||
['ring', '--default-ring-width'],
|
||||
['ring-3', '--ring-width-3'],
|
||||
])
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,16 @@ test('config values can be merged into the theme', () => {
|
|||
normal: '0 1px 3px black',
|
||||
},
|
||||
|
||||
borderWidth: {
|
||||
DEFAULT: '1.5px',
|
||||
},
|
||||
outlineWidth: {
|
||||
DEFAULT: '2.5px',
|
||||
},
|
||||
ringWidth: {
|
||||
DEFAULT: '3.5px',
|
||||
},
|
||||
|
||||
borderRadius: {
|
||||
sm: '0.33rem',
|
||||
},
|
||||
|
|
@ -57,6 +67,10 @@ test('config values can be merged into the theme', () => {
|
|||
'2xl': ['2rem'],
|
||||
},
|
||||
|
||||
ringColor: {
|
||||
DEFAULT: '#fff',
|
||||
},
|
||||
|
||||
letterSpacing: {
|
||||
superWide: '0.25em',
|
||||
},
|
||||
|
|
@ -77,8 +91,12 @@ test('config values can be merged into the theme', () => {
|
|||
},
|
||||
|
||||
transitionTimingFunction: {
|
||||
DEFAULT: 'ease-in-out',
|
||||
fast: 'cubic-bezier(0, 0.55, 0.45, 1)',
|
||||
},
|
||||
transitionDuration: {
|
||||
DEFAULT: '1234ms',
|
||||
},
|
||||
},
|
||||
},
|
||||
base: '/root',
|
||||
|
|
@ -125,6 +143,23 @@ test('config values can be merged into the theme', () => {
|
|||
expect(theme.resolve('100%', ['--width'])).toEqual('100%')
|
||||
expect(theme.resolve('9xs', ['--container'])).toEqual('6rem')
|
||||
expect(theme.resolve('fast', ['--ease'])).toEqual('cubic-bezier(0, 0.55, 0.45, 1)')
|
||||
|
||||
expect(theme.get(['--border'])).toEqual(null)
|
||||
expect(theme.get(['--default-border-width'])).toEqual('1.5px')
|
||||
|
||||
expect(theme.get(['--outline'])).toEqual(null)
|
||||
expect(theme.get(['--default-outline-width'])).toEqual('2.5px')
|
||||
|
||||
expect(theme.get(['--ring-color'])).toEqual(null)
|
||||
expect(theme.get(['--default-ring-color'])).toEqual('#fff')
|
||||
|
||||
expect(theme.get(['--ring-width'])).toEqual(null)
|
||||
expect(theme.get(['--default-ring-width'])).toEqual('3.5px')
|
||||
|
||||
expect(theme.get(['--default-transition-duration'])).toEqual('1234ms')
|
||||
|
||||
expect(theme.get(['--ease'])).toEqual(null)
|
||||
expect(theme.get(['--default-transition-timing-function'])).toEqual('ease-in-out')
|
||||
})
|
||||
|
||||
test('will reset default theme values with overwriting theme values', () => {
|
||||
|
|
|
|||
|
|
@ -147,32 +147,57 @@ export function themeableValues(config: ResolvedConfig['theme']): [string[], unk
|
|||
return toAdd
|
||||
}
|
||||
|
||||
const SPECIAL_DEFAULT_KEYS: Record<string, string> = {
|
||||
borderWidth: 'border-width',
|
||||
outlineWidth: 'outline-width',
|
||||
ringColor: 'ring-color',
|
||||
ringWidth: 'ring-width',
|
||||
transitionDuration: 'transition-duration',
|
||||
transitionTimingFunction: 'transition-timing-function',
|
||||
}
|
||||
|
||||
const OLD_TO_NEW_NAMESPACE: Record<string, string> = {
|
||||
animation: 'animate',
|
||||
aspectRatio: 'aspect',
|
||||
borderRadius: 'radius',
|
||||
boxShadow: 'shadow',
|
||||
colors: 'color',
|
||||
containers: 'container',
|
||||
fontFamily: 'font',
|
||||
fontSize: 'text',
|
||||
letterSpacing: 'tracking',
|
||||
lineHeight: 'leading',
|
||||
maxWidth: 'container',
|
||||
screens: 'breakpoint',
|
||||
transitionTimingFunction: 'ease',
|
||||
}
|
||||
|
||||
const IS_VALID_KEY = /^[a-zA-Z0-9-_%/\.]+$/
|
||||
|
||||
export function keyPathToCssProperty(path: string[]) {
|
||||
// In some special cases the `DEFAULT` key did not map to a "default" utility
|
||||
// e.g. `ringColor.DEFAULT` wasn't *just* used for `ring`. It was used for
|
||||
// all ring utilities as the color when one wasn't specified.
|
||||
//
|
||||
// We place these specialty values under the `--default-*` namespace to signal
|
||||
// that they are defaults used by (potentially) multiple utilities.
|
||||
let specialDefault = SPECIAL_DEFAULT_KEYS[path[0]]
|
||||
if (specialDefault && path[1] === 'DEFAULT') return `default-${specialDefault}`
|
||||
|
||||
// The legacy container component config should not be included in the Theme
|
||||
if (path[0] === 'container') return null
|
||||
|
||||
path = path.slice()
|
||||
|
||||
if (path[0] === 'animation') path[0] = 'animate'
|
||||
if (path[0] === 'aspectRatio') path[0] = 'aspect'
|
||||
if (path[0] === 'borderRadius') path[0] = 'radius'
|
||||
if (path[0] === 'boxShadow') path[0] = 'shadow'
|
||||
if (path[0] === 'colors') path[0] = 'color'
|
||||
if (path[0] === 'containers') path[0] = 'container'
|
||||
if (path[0] === 'fontFamily') path[0] = 'font'
|
||||
if (path[0] === 'fontSize') path[0] = 'text'
|
||||
if (path[0] === 'letterSpacing') path[0] = 'tracking'
|
||||
if (path[0] === 'lineHeight') path[0] = 'leading'
|
||||
if (path[0] === 'maxWidth') path[0] = 'container'
|
||||
if (path[0] === 'screens') path[0] = 'breakpoint'
|
||||
if (path[0] === 'transitionTimingFunction') path[0] = 'ease'
|
||||
|
||||
for (let part of path) {
|
||||
if (!IS_VALID_KEY.test(part)) return null
|
||||
}
|
||||
|
||||
// Map old v3 namespaces to new theme namespaces
|
||||
let ns = OLD_TO_NEW_NAMESPACE[path[0]]
|
||||
if (ns) {
|
||||
path = path.slice()
|
||||
path[0] = ns
|
||||
}
|
||||
|
||||
return (
|
||||
path
|
||||
// [1] should move into the nested object tuple. To create the CSS variable
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue