Skip to content

Commit 4a4be27

Browse files
authored
Migrate theme(…) to var(…) in CSS (tailwindlabs#14695)
This PR is a follow up from tailwindlabs#14664 migrates all the `theme(…)` calls in your CSS to `var(…)` if we can. In at-rules, like `@media` we can't use `var(…)` so we have to use the modern version of `theme(…)`. In declarations, we can convert to `var(…)` unless there is a modifier used in the `theme(…)` function, then we can only convert to the new `theme(…)` syntax without dot notation. Input: ```css @media theme(spacing.4) { .foo { background-color: theme(colors.red.900); color: theme(colors.red.900 / 75%); /* With spaces around the `/` */ border-color: theme(colors.red.200/75%); /* Without spaces around the `/` */ } } ``` Output: ```css @media theme(--spacing-4) { /* ^^^^^^^^^^^^^^^^^^ Use `theme(…)` since `var(…)` is invalid in this position*/ .foo { background-color: var(--color-red-900); /* Converted to var(…) */ color: theme(--color-red-900 / 75%); /* Convert to modern theme(…) */ border-color: theme(--color-red-200 / 75%); /* Convert to modern theme(…) — pretty printed*/ } } ```
1 parent aff858a commit 4a4be27

File tree

5 files changed

+85
-4
lines changed

5 files changed

+85
-4
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Add first draft of new wide-gamut color palette ([#14693](https://github.com/tailwindlabs/tailwindcss/pull/14693))
13-
- _Upgrade (experimental)_: Migrate `theme(…)` calls in classes to `var(…)` or to the modern `theme(…)` syntax ([#14664](https://github.com/tailwindlabs/tailwindcss/pull/14664))
13+
- _Upgrade (experimental)_: Migrate `theme(…)` calls to `var(…)` or to the modern `theme(…)` syntax ([#14664](https://github.com/tailwindlabs/tailwindcss/pull/14664), [#14695](https://github.com/tailwindlabs/tailwindcss/pull/14695))
1414

1515
### Fixed
1616

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { __unstable__loadDesignSystem } from '@tailwindcss/node'
2+
import dedent from 'dedent'
3+
import postcss from 'postcss'
4+
import { expect, it } from 'vitest'
5+
import { formatNodes } from './format-nodes'
6+
import { migrateThemeToVar } from './migrate-theme-to-var'
7+
8+
const css = dedent
9+
10+
async function migrate(input: string) {
11+
return postcss()
12+
.use(
13+
migrateThemeToVar({
14+
designSystem: await __unstable__loadDesignSystem(`@import 'tailwindcss';`, {
15+
base: __dirname,
16+
}),
17+
}),
18+
)
19+
.use(formatNodes())
20+
.process(input, { from: expect.getState().testPath })
21+
.then((result) => result.css)
22+
}
23+
24+
it('should migrate `theme(…)` to `var(…)`', async () => {
25+
expect(
26+
await migrate(css`
27+
@media theme(spacing.4) {
28+
.foo {
29+
background-color: theme(colors.red.900);
30+
color: theme(colors.red.900 / 75%);
31+
border-color: theme(colors.red.200/75%);
32+
}
33+
}
34+
`),
35+
).toMatchInlineSnapshot(`
36+
"@media theme(--spacing-4) {
37+
.foo {
38+
background-color: var(--color-red-900);
39+
color: theme(--color-red-900 / 75%);
40+
border-color: theme(--color-red-200 / 75%);
41+
}
42+
}"
43+
`)
44+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { type Plugin } from 'postcss'
2+
import type { DesignSystem } from '../../../tailwindcss/src/design-system'
3+
import { Convert, createConverter } from '../template/codemods/theme-to-var'
4+
5+
export function migrateThemeToVar({
6+
designSystem,
7+
}: {
8+
designSystem?: DesignSystem
9+
} = {}): Plugin {
10+
return {
11+
postcssPlugin: '@tailwindcss/upgrade/migrate-theme-to-var',
12+
OnceExit(root) {
13+
if (!designSystem) return
14+
let convert = createConverter(designSystem, { prettyPrint: true })
15+
16+
root.walkDecls((decl) => {
17+
let [newValue] = convert(decl.value)
18+
decl.value = newValue
19+
})
20+
21+
root.walkAtRules((atRule) => {
22+
if (
23+
atRule.name === 'media' ||
24+
atRule.name === 'custom-media' ||
25+
atRule.name === 'container' ||
26+
atRule.name === 'supports'
27+
) {
28+
let [newValue] = convert(atRule.params, Convert.MigrateThemeOnly)
29+
atRule.params = newValue
30+
}
31+
})
32+
},
33+
}
34+
}

packages/@tailwindcss-upgrade/src/migrate.ts

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { migrateConfig } from './codemods/migrate-config'
1010
import { migrateMediaScreen } from './codemods/migrate-media-screen'
1111
import { migrateMissingLayers } from './codemods/migrate-missing-layers'
1212
import { migrateTailwindDirectives } from './codemods/migrate-tailwind-directives'
13+
import { migrateThemeToVar } from './codemods/migrate-theme-to-var'
1314
import type { JSConfigMigration } from './migrate-js-config'
1415
import { Stylesheet, type StylesheetConnection, type StylesheetId } from './stylesheet'
1516
import { resolveCssId } from './utils/resolve'
@@ -35,6 +36,7 @@ export async function migrateContents(
3536

3637
return postcss()
3738
.use(migrateAtApply(options))
39+
.use(migrateThemeToVar(options))
3840
.use(migrateMediaScreen(options))
3941
.use(migrateAtLayerUtilities(stylesheet))
4042
.use(migrateMissingLayers())

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

+4-3
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export function themeToVar(
8080
return rawCandidate
8181
}
8282

83-
export function createConverter(designSystem: DesignSystem) {
83+
export function createConverter(designSystem: DesignSystem, { prettyPrint = false } = {}) {
8484
function convert(input: string, options = Convert.All): [string, CandidateModifier | null] {
8585
let ast = ValueParser.parse(input)
8686

@@ -110,7 +110,7 @@ export function createConverter(designSystem: DesignSystem) {
110110
}
111111

112112
// If we see a `/`, we have a modifier
113-
else if (child.kind === 'separator' && child.value === '/') {
113+
else if (child.kind === 'separator' && child.value.trim() === '/') {
114114
themeModifierCount += 1
115115
return ValueParser.ValueWalkAction.Stop
116116
}
@@ -211,7 +211,8 @@ export function createConverter(designSystem: DesignSystem) {
211211
let variable = pathToVariableName(path)
212212
if (!variable) return null
213213

214-
let modifier = parts.length > 0 ? `/${parts.join('/')}` : ''
214+
let modifier =
215+
parts.length > 0 ? (prettyPrint ? ` / ${parts.join(' / ')}` : `/${parts.join('/')}`) : ''
215216
return fallback ? `theme(${variable}${modifier}, ${fallback})` : `theme(${variable}${modifier})`
216217
}
217218

0 commit comments

Comments
 (0)