Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- _Upgrade (experimental)_: Migrate v3 PostCSS setups to v4 in some cases ([#14612](https://github.com/tailwindlabs/tailwindcss/pull/14612))
- _Upgrade (experimental)_: Automatically discover JavaScript config files ([#14597](https://github.com/tailwindlabs/tailwindcss/pull/14597))
- _Upgrade (experimental)_: Migrate legacy classes to the v4 alternative ([#14643](https://github.com/tailwindlabs/tailwindcss/pull/14643))
- _Upgrade (experimental)_: Migrate static JS configurations to CSS ([#14639](https://github.com/tailwindlabs/tailwindcss/pull/14639), [#14650](https://github.com/tailwindlabs/tailwindcss/pull/14650), [#14648](https://github.com/tailwindlabs/tailwindcss/pull/14648))
- _Upgrade (experimental)_: Migrate static JS configurations to CSS ([#14639](https://github.com/tailwindlabs/tailwindcss/pull/14639), [#14650](https://github.com/tailwindlabs/tailwindcss/pull/14650), [#14648](https://github.com/tailwindlabs/tailwindcss/pull/14648), [#14666](https://github.com/tailwindlabs/tailwindcss/pull/14666))
- _Upgrade (experimental)_: Migrate `@media screen(…)` when running codemods ([#14603](https://github.com/tailwindlabs/tailwindcss/pull/14603))
- _Upgrade (experimental)_: Inject `@config "…"` when a `tailwind.config.{js,ts,…}` is detected ([#14635](https://github.com/tailwindlabs/tailwindcss/pull/14635))
- _Upgrade (experimental)_: Migrate `aria-*`, `data-*`, and `supports-*` variants from arbitrary values to bare values ([#14644](https://github.com/tailwindlabs/tailwindcss/pull/14644))
Expand Down
37 changes: 36 additions & 1 deletion integrations/upgrade/js-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ test(
borderRadius: {
'4xl': '2rem',
},
keyframes: {
'spin-clockwise': {
'0%': { transform: 'rotate(0deg)' },
'100%': { transform: 'rotate(360deg)' },
},
'spin-counterclockwise': {
'0%': { transform: 'rotate(0deg)' },
'100%': { transform: 'rotate(-360deg)' },
},
},
animation: {
'spin-clockwise': 'spin-clockwise 1s linear infinite',
'spin-counterclockwise': 'spin-counterclockwise 1s linear infinite',
},
},
},
plugins: [],
Expand Down Expand Up @@ -91,9 +105,30 @@ test(
--font-size-base--line-height: 2rem;

--font-family-sans: Inter, system-ui, sans-serif;
--font-family-display: Cabinet Grotesk, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--font-family-display: Cabinet Grotesk, ui-sans-serif, system-ui, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";

--radius-4xl: 2rem;

--animate-spin-clockwise: spin-clockwise 1s linear infinite;
--animate-spin-counterclockwise: spin-counterclockwise 1s linear infinite;

@keyframes spin-clockwise {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes spin-counterclockwise {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(-360deg);
}
}
}
"
`)
Expand Down
5 changes: 4 additions & 1 deletion packages/@tailwindcss-upgrade/src/codemods/migrate-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export function migrateConfig(
if (!sheet.file) return

let cssConfig = new AtRule()
cssConfig.raws.tailwind_pretty = true

if (jsConfigMigration === null) {
// Skip if there is already a `@config` directive
Expand Down Expand Up @@ -85,6 +84,10 @@ export function migrateConfig(
return WalkAction.Skip
})

for (let node of cssConfig?.nodes ?? []) {
node.raws.tailwind_pretty = true
}

if (!locationNode) {
root.prepend(cssConfig.nodes)
} else if (locationNode.name === 'import') {
Expand Down
27 changes: 26 additions & 1 deletion packages/@tailwindcss-upgrade/src/migrate-js-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import type { Config } from 'tailwindcss'
import defaultTheme from 'tailwindcss/defaultTheme'
import { fileURLToPath } from 'url'
import { loadModule } from '../../@tailwindcss-node/src/compile'
import { toCss, type AstNode } from '../../tailwindcss/src/ast'
import {
keyPathToCssProperty,
themeableValues,
} from '../../tailwindcss/src/compat/apply-config-to-theme'
import { applyKeyframesToAst } from '../../tailwindcss/src/compat/apply-keyframes-to-ast'
import { deepMerge } from '../../tailwindcss/src/compat/config/deep-merge'
import { mergeThemeExtension } from '../../tailwindcss/src/compat/config/resolve-config'
import type { ThemeConfig } from '../../tailwindcss/src/compat/config/types'
Expand Down Expand Up @@ -89,7 +91,11 @@ async function migrateTheme(unresolvedConfig: Config & { theme: any }): Promise<
}
}

let themeValues = deepMerge({}, [overwriteTheme, extendTheme], mergeThemeExtension)
let themeValues: Record<string, Record<string, unknown>> = deepMerge(
{},
[overwriteTheme, extendTheme],
mergeThemeExtension,
)

let prevSectionKey = ''

Expand All @@ -99,6 +105,10 @@ async function migrateTheme(unresolvedConfig: Config & { theme: any }): Promise<
if (typeof value !== 'string' && typeof value !== 'number') {
continue
}

if (key[0] === 'keyframes') {
continue
}
containsThemeKeys = true

let sectionKey = createSectionKey(key)
Expand All @@ -115,6 +125,15 @@ async function migrateTheme(unresolvedConfig: Config & { theme: any }): Promise<
css += ` --${keyPathToCssProperty(key)}: ${value};\n`
}

if ('keyframes' in themeValues) {
containsThemeKeys = true
css += '\n' + keyframesToCss(themeValues.keyframes)
}
if ('extends' in themeValues && 'keyframes' in themeValues.extends) {
containsThemeKeys = true
css += '\n' + keyframesToCss(themeValues.extends.keyframes as Record<string, unknown>)
}

if (!containsThemeKeys) {
return null
}
Expand Down Expand Up @@ -232,3 +251,9 @@ function onlyUsesAllowedTopLevelKeys(theme: ThemeConfig): boolean {
}
return true
}

function keyframesToCss(keyframes: Record<string, unknown>): string {
let ast: AstNode[] = []
applyKeyframesToAst(ast, { theme: { keyframes } })
return toCss(ast).trim() + '\n'
Comment on lines +252 to +254
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a sneaky way of handling this. I love it!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for real this is a pretty sweet way of doing it 💯

}
2 changes: 1 addition & 1 deletion packages/tailwindcss/src/compat/apply-keyframes-to-ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { rule, type AstNode } from '../ast'
import type { ResolvedConfig } from './config/types'
import { objectToAst } from './plugin-api'

export function applyKeyframesToAst(ast: AstNode[], { theme }: ResolvedConfig) {
export function applyKeyframesToAst(ast: AstNode[], { theme }: Pick<ResolvedConfig, 'theme'>) {
if ('keyframes' in theme) {
for (let [name, keyframe] of Object.entries(theme.keyframes)) {
ast.push(rule(`@keyframes ${name}`, objectToAst(keyframe as any)))
Expand Down