mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
Migrate theme(…) to var(…) in CSS (#14695)
This PR is a follow up from https://github.com/tailwindlabs/tailwindcss/pull/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*/ } } ```
This commit is contained in:
parent
aff858a3e6
commit
4a4be27dc1
@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Added
|
||||
|
||||
- Add first draft of new wide-gamut color palette ([#14693](https://github.com/tailwindlabs/tailwindcss/pull/14693))
|
||||
- _Upgrade (experimental)_: Migrate `theme(…)` calls in classes to `var(…)` or to the modern `theme(…)` syntax ([#14664](https://github.com/tailwindlabs/tailwindcss/pull/14664))
|
||||
- _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))
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@ -0,0 +1,44 @@
|
||||
import { __unstable__loadDesignSystem } from '@tailwindcss/node'
|
||||
import dedent from 'dedent'
|
||||
import postcss from 'postcss'
|
||||
import { expect, it } from 'vitest'
|
||||
import { formatNodes } from './format-nodes'
|
||||
import { migrateThemeToVar } from './migrate-theme-to-var'
|
||||
|
||||
const css = dedent
|
||||
|
||||
async function migrate(input: string) {
|
||||
return postcss()
|
||||
.use(
|
||||
migrateThemeToVar({
|
||||
designSystem: await __unstable__loadDesignSystem(`@import 'tailwindcss';`, {
|
||||
base: __dirname,
|
||||
}),
|
||||
}),
|
||||
)
|
||||
.use(formatNodes())
|
||||
.process(input, { from: expect.getState().testPath })
|
||||
.then((result) => result.css)
|
||||
}
|
||||
|
||||
it('should migrate `theme(…)` to `var(…)`', async () => {
|
||||
expect(
|
||||
await migrate(css`
|
||||
@media theme(spacing.4) {
|
||||
.foo {
|
||||
background-color: theme(colors.red.900);
|
||||
color: theme(colors.red.900 / 75%);
|
||||
border-color: theme(colors.red.200/75%);
|
||||
}
|
||||
}
|
||||
`),
|
||||
).toMatchInlineSnapshot(`
|
||||
"@media theme(--spacing-4) {
|
||||
.foo {
|
||||
background-color: var(--color-red-900);
|
||||
color: theme(--color-red-900 / 75%);
|
||||
border-color: theme(--color-red-200 / 75%);
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
@ -0,0 +1,34 @@
|
||||
import { type Plugin } from 'postcss'
|
||||
import type { DesignSystem } from '../../../tailwindcss/src/design-system'
|
||||
import { Convert, createConverter } from '../template/codemods/theme-to-var'
|
||||
|
||||
export function migrateThemeToVar({
|
||||
designSystem,
|
||||
}: {
|
||||
designSystem?: DesignSystem
|
||||
} = {}): Plugin {
|
||||
return {
|
||||
postcssPlugin: '@tailwindcss/upgrade/migrate-theme-to-var',
|
||||
OnceExit(root) {
|
||||
if (!designSystem) return
|
||||
let convert = createConverter(designSystem, { prettyPrint: true })
|
||||
|
||||
root.walkDecls((decl) => {
|
||||
let [newValue] = convert(decl.value)
|
||||
decl.value = newValue
|
||||
})
|
||||
|
||||
root.walkAtRules((atRule) => {
|
||||
if (
|
||||
atRule.name === 'media' ||
|
||||
atRule.name === 'custom-media' ||
|
||||
atRule.name === 'container' ||
|
||||
atRule.name === 'supports'
|
||||
) {
|
||||
let [newValue] = convert(atRule.params, Convert.MigrateThemeOnly)
|
||||
atRule.params = newValue
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,7 @@ import { migrateConfig } from './codemods/migrate-config'
|
||||
import { migrateMediaScreen } from './codemods/migrate-media-screen'
|
||||
import { migrateMissingLayers } from './codemods/migrate-missing-layers'
|
||||
import { migrateTailwindDirectives } from './codemods/migrate-tailwind-directives'
|
||||
import { migrateThemeToVar } from './codemods/migrate-theme-to-var'
|
||||
import type { JSConfigMigration } from './migrate-js-config'
|
||||
import { Stylesheet, type StylesheetConnection, type StylesheetId } from './stylesheet'
|
||||
import { resolveCssId } from './utils/resolve'
|
||||
@ -35,6 +36,7 @@ export async function migrateContents(
|
||||
|
||||
return postcss()
|
||||
.use(migrateAtApply(options))
|
||||
.use(migrateThemeToVar(options))
|
||||
.use(migrateMediaScreen(options))
|
||||
.use(migrateAtLayerUtilities(stylesheet))
|
||||
.use(migrateMissingLayers())
|
||||
|
||||
@ -80,7 +80,7 @@ export function themeToVar(
|
||||
return rawCandidate
|
||||
}
|
||||
|
||||
export function createConverter(designSystem: DesignSystem) {
|
||||
export function createConverter(designSystem: DesignSystem, { prettyPrint = false } = {}) {
|
||||
function convert(input: string, options = Convert.All): [string, CandidateModifier | null] {
|
||||
let ast = ValueParser.parse(input)
|
||||
|
||||
@ -110,7 +110,7 @@ export function createConverter(designSystem: DesignSystem) {
|
||||
}
|
||||
|
||||
// If we see a `/`, we have a modifier
|
||||
else if (child.kind === 'separator' && child.value === '/') {
|
||||
else if (child.kind === 'separator' && child.value.trim() === '/') {
|
||||
themeModifierCount += 1
|
||||
return ValueParser.ValueWalkAction.Stop
|
||||
}
|
||||
@ -211,7 +211,8 @@ export function createConverter(designSystem: DesignSystem) {
|
||||
let variable = pathToVariableName(path)
|
||||
if (!variable) return null
|
||||
|
||||
let modifier = parts.length > 0 ? `/${parts.join('/')}` : ''
|
||||
let modifier =
|
||||
parts.length > 0 ? (prettyPrint ? ` / ${parts.join(' / ')}` : `/${parts.join('/')}`) : ''
|
||||
return fallback ? `theme(${variable}${modifier}, ${fallback})` : `theme(${variable}${modifier})`
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user