Skip to content

Commit 31c0a21

Browse files
authored
Improve upgrade migrations (#18184)
This PR fixes 2 issues with the migration tool where certain classes weren't migrated. This PR fixes those 2 scenarios: ### Scenario 1 When you have an arbitrary opacity modifier that doesn't use `%`, but is just a number typically between `0` and `1` then this was not converted to the bare value equivalent before. E.g.: ```html <div class="bg-[#f00]/[0.16]"></dv> ``` Will now be converted to: ```html <div class="bg-[#f00]/16"></dv> ``` ### Scenario 2 Fixes a bug when a CSS function was used in a fallback value in the CSS variable shorthand syntax. In that case we didn't migrate the class to the new syntax. This was because we assumed that a `(` was found, that we are dealing with a CSS function. E.g.: ```html <div class="w-[--spacing(1)]"></div> ^ This indicates a CSS function, we should not be converting this to `w-(--spacing(1))` ``` But if a function was used as a fallback value, for example: ```html <div class="bg-[--my-color,theme(colors.red.500)]"></dv> ``` Then we also didn't migrate it, but since the function call is in the fallback, we can still migrate it. Will now properly be converted to: ```html <div class="bg-(--my-color,var(--color-red-500))"></dv> ``` ## Test plan 1. Added a test for the first case 2. Added a test for the second case 3. Also added an integration-like test that runs all the migration steps to make sure that the `theme(…)` in the fallback also gets updated to `var(…)`. This one caught an issue because the `var(…)` wasn't handling prefixes correctly.
1 parent 3c629de commit 31c0a21

File tree

7 files changed

+56
-4
lines changed

7 files changed

+56
-4
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
- Nothing yet!
10+
### Fixed
11+
12+
- Upgrade: migrate arbitrary modifiers with values without percentage sign to bare values `/[0.16]` -> `/16` ([#18184](https://github.com/tailwindlabs/tailwindcss/pull/18184))
13+
- Upgrade: migrate CSS variable shorthand if fallback value contains function call ([#18184](https://github.com/tailwindlabs/tailwindcss/pull/18184))
1114

1215
## [4.1.8] - 2025-05-27
1316

packages/@tailwindcss-upgrade/src/codemods/template/migrate-automatic-var-injection.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ test.each([
4040
['w-[--spacing(5)]', 'w-[--spacing(5)]'],
4141
['bg-[--theme(--color-red-500)]', 'bg-[--theme(--color-red-500)]'],
4242

43+
// Fallback values should be included inside the `var(…)` function
44+
['bg-[--my-color,red]', 'bg-(--my-color,red)'],
45+
// Fallback values can contain CSS functions
46+
['bg-[--my-color,theme(spacing.1)]', 'bg-(--my-color,theme(spacing.1))'],
47+
4348
// Some properties never had var() injection in v3.
4449
['[scroll-timeline-name:--myTimeline]', '[scroll-timeline-name:--myTimeline]'],
4550
['[timeline-scope:--myScope]', '[timeline-scope:--myScope]'],

packages/@tailwindcss-upgrade/src/codemods/template/migrate-automatic-var-injection.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { walk, WalkAction } from '../../../../tailwindcss/src/ast'
22
import { type Candidate, type Variant } from '../../../../tailwindcss/src/candidate'
33
import type { Config } from '../../../../tailwindcss/src/compat/plugin-api'
44
import type { DesignSystem } from '../../../../tailwindcss/src/design-system'
5+
import * as ValueParser from '../../../../tailwindcss/src/value-parser'
56

67
export function migrateAutomaticVarInjection(
78
designSystem: DesignSystem,
@@ -73,9 +74,23 @@ export function migrateAutomaticVarInjection(
7374

7475
function injectVar(value: string): { value: string; didChange: boolean } {
7576
let didChange = false
76-
if (value.startsWith('--') && !value.includes('(')) {
77-
value = `var(${value})`
78-
didChange = true
77+
if (value.startsWith('--')) {
78+
// E.g.:
79+
//
80+
// - `--my-color` → `var(--my-color)` Convert variable
81+
// - `--my-color,red` → `var(--my-color,red)` Convert variable with fallback
82+
// - `--theme(color.red)` → `--theme(color.red)` Do not convert functions
83+
//
84+
if (
85+
// No `(` definitely means there is no function
86+
!value.includes('(') ||
87+
// There could be a function call in the fallback value, but it cannot be
88+
// top-level, so we can safely check the first part
89+
ValueParser.parse(value)[0]?.kind !== 'function'
90+
) {
91+
value = `var(${value})`
92+
didChange = true
93+
}
7994
} else if (value.startsWith(' --')) {
8095
value = value.slice(1)
8196
didChange = true

packages/@tailwindcss-upgrade/src/codemods/template/migrate-optimize-modifier.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ describe.each([['default'], ['with-variant'], ['important'], ['prefix']])('%s',
3737
// Use a bare value modifier
3838
['bg-red-500/[25%]', 'bg-red-500/25'],
3939

40+
// Convert 0-1 values to bare values
41+
['bg-[#f00]/[0.16]', 'bg-[#f00]/16'],
42+
4043
// Drop unnecessary modifiers
4144
['bg-red-500/[100%]', 'bg-red-500'],
4245
['bg-red-500/100', 'bg-red-500'],

packages/@tailwindcss-upgrade/src/codemods/template/migrate-optimize-modifier.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,23 @@ export function migrateOptimizeModifier(
5555
}
5656
}
5757

58+
// 3. Try to remove the square brackets, but multiply by 100. E.g.: `[0.16]` -> `16`
59+
if (!changed) {
60+
let newModifier: NamedUtilityValue = {
61+
kind: 'named',
62+
value: `${parseFloat(modifier.value) * 100}`,
63+
fraction: null,
64+
}
65+
66+
if (
67+
targetSignature ===
68+
signatures.get(designSystem.printCandidate({ ...candidate, modifier: newModifier }))
69+
) {
70+
changed = true
71+
candidate.modifier = newModifier
72+
}
73+
}
74+
5875
return changed ? designSystem.printCandidate(candidate) : rawCandidate
5976
}
6077
}

packages/@tailwindcss-upgrade/src/codemods/template/migrate-theme-to-var.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ export function createConverter(designSystem: DesignSystem, { prettyPrint = fals
193193
let variable = `--${keyPathToCssProperty(toKeyPath(path))}` as const
194194
if (!designSystem.theme.get([variable])) return null
195195

196+
if (designSystem.theme.prefix) {
197+
return `--${designSystem.theme.prefix}-${variable.slice(2)}`
198+
}
199+
196200
return variable
197201
}
198202

packages/@tailwindcss-upgrade/src/codemods/template/migrate.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ describe.each([['default'], ['with-variant'], ['important'], ['prefix']])('%s',
6464
// non-negative version first so we can replace `-mt-[0px]` with `mt-[0px]`.
6565
['mt-[0px]', 'mt-[0px]'],
6666
['-mt-[0px]', 'mt-[0px]'],
67+
68+
// Shorthand CSS Variables should be converted to the new syntax, even if
69+
// the fallback contains functions. The fallback should also be migrated to
70+
// the newest syntax.
71+
['bg-[--my-color,theme(colors.red.500)]', 'bg-(--my-color,var(--color-red-500))'],
6772
])(testName, async (candidate, result) => {
6873
if (strategy === 'with-variant') {
6974
candidate = `focus:${candidate}`

0 commit comments

Comments
 (0)