diff --git a/CHANGELOG.md b/CHANGELOG.md index cdb0386c75d7..a6aada039ea0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `lightningcss` now statically links Visual Studio redistributables ([#17979](https://github.com/tailwindlabs/tailwindcss/pull/17979)) - Ensure that running the Standalone build does not leave temporary files behind ([#17981](https://github.com/tailwindlabs/tailwindcss/pull/17981)) - Fix `-rotate-*` utilities with arbitrary values ([#18014](https://github.com/tailwindlabs/tailwindcss/pull/18014)) +- Upgrade: Change casing of utilities with named values to kebab-case to match updated theme variables ([#18017](https://github.com/tailwindlabs/tailwindcss/pull/18017)) ### Added diff --git a/integrations/upgrade/js-config.test.ts b/integrations/upgrade/js-config.test.ts index d2240fbf03b6..93d239af0280 100644 --- a/integrations/upgrade/js-config.test.ts +++ b/integrations/upgrade/js-config.test.ts @@ -30,9 +30,13 @@ test( 400: '#f87171', 500: 'red', }, + superRed: '#ff0000', steel: 'rgb(70 130 180 / )', smoke: 'rgba(245, 245, 245, var(--smoke-alpha, ))', }, + opacity: { + superOpaque: '0.95', + }, fontSize: { xs: ['0.75rem', { lineHeight: '1rem' }], sm: ['0.875rem', { lineHeight: '1.5rem' }], @@ -144,9 +148,10 @@ test( } `, 'src/index.html': html` -
+
`, 'node_modules/my-external-lib/src/template.html': html`
@@ -162,8 +167,9 @@ test( " --- src/index.html ---
+ class="[letter-spacing:var(--tracking-super-wide)] [line-height:var(--leading-super-loose)]" + >
+
--- src/input.css --- @import 'tailwindcss'; @@ -181,9 +187,13 @@ test( --color-red-500: #ef4444; --color-red-600: #dc2626; + --color-super-red: #ff0000; --color-steel: rgb(70 130 180); --color-smoke: rgba(245, 245, 245, var(--smoke-alpha, 1)); + --opacity-*: initial; + --opacity-super-opaque: 95%; + --text-*: initial; --text-xs: 0.75rem; --text-xs--line-height: 1rem; diff --git a/packages/@tailwindcss-upgrade/src/codemods/template/migrate-camelcase-in-named-value.test.ts b/packages/@tailwindcss-upgrade/src/codemods/template/migrate-camelcase-in-named-value.test.ts new file mode 100644 index 000000000000..ed5be23c0cc4 --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/codemods/template/migrate-camelcase-in-named-value.test.ts @@ -0,0 +1,24 @@ +import { __unstable__loadDesignSystem } from '@tailwindcss/node' +import { expect, test, vi } from 'vitest' +import * as versions from '../../utils/version' +import { migrateCamelcaseInNamedValue } from './migrate-camelcase-in-named-value' +vi.spyOn(versions, 'isMajor').mockReturnValue(true) + +test.each([ + ['text-superRed', 'text-super-red'], + ['text-red/superOpaque', 'text-red/super-opaque'], + ['text-superRed/superOpaque', 'text-super-red/super-opaque'], + + // Should not migrate named values in modifiers + ['group-hover/superGroup:underline', 'group-hover/superGroup:underline'], + + ['hover:text-superRed', 'hover:text-super-red'], + ['hover:text-red/superOpaque', 'hover:text-red/super-opaque'], + ['hover:text-superRed/superOpaque', 'hover:text-super-red/super-opaque'], +])('%s => %s', async (candidate, result) => { + let designSystem = await __unstable__loadDesignSystem('@import "tailwindcss";', { + base: __dirname, + }) + + expect(migrateCamelcaseInNamedValue(designSystem, {}, candidate)).toEqual(result) +}) diff --git a/packages/@tailwindcss-upgrade/src/codemods/template/migrate-camelcase-in-named-value.ts b/packages/@tailwindcss-upgrade/src/codemods/template/migrate-camelcase-in-named-value.ts new file mode 100644 index 000000000000..2214a8366cef --- /dev/null +++ b/packages/@tailwindcss-upgrade/src/codemods/template/migrate-camelcase-in-named-value.ts @@ -0,0 +1,53 @@ +import type { Config } from '../../../../tailwindcss/src/compat/plugin-api' +import type { DesignSystem } from '../../../../tailwindcss/src/design-system' +import * as version from '../../utils/version' + +// Converts named values to use kebab-case. This is necessary because the +// upgrade tool also renames the theme values to kebab-case, so `text-superRed` +// will have its theme value renamed to `--color-super-red` and thus the utility +// will be renamed to `text-super-red`. +export function migrateCamelcaseInNamedValue( + designSystem: DesignSystem, + _userConfig: Config | null, + rawCandidate: string, +): string { + if (!version.isMajor(3)) return rawCandidate + + for (let candidate of designSystem.parseCandidate(rawCandidate)) { + if (candidate.kind !== 'functional') continue + let clone = structuredClone(candidate) + let didChange = false + + if ( + candidate.value && + clone.value && + candidate.value.kind === 'named' && + clone.value.kind === 'named' && + candidate.value.value.match(/[A-Z]/) + ) { + clone.value.value = camelToKebab(candidate.value.value) + didChange = true + } + + if ( + candidate.modifier && + clone.modifier && + candidate.modifier.kind === 'named' && + clone.modifier.kind === 'named' && + candidate.modifier.value.match(/[A-Z]/) + ) { + clone.modifier.value = camelToKebab(candidate.modifier.value) + didChange = true + } + + if (didChange) { + return designSystem.printCandidate(clone) + } + } + + return rawCandidate +} + +function camelToKebab(str: string): string { + return str.replace(/([A-Z])/g, '-$1').toLowerCase() +} diff --git a/packages/@tailwindcss-upgrade/src/codemods/template/migrate.ts b/packages/@tailwindcss-upgrade/src/codemods/template/migrate.ts index 594342a3f58f..fff859c0ad1f 100644 --- a/packages/@tailwindcss-upgrade/src/codemods/template/migrate.ts +++ b/packages/@tailwindcss-upgrade/src/codemods/template/migrate.ts @@ -11,6 +11,7 @@ import { migrateArbitraryVariants } from './migrate-arbitrary-variants' import { migrateAutomaticVarInjection } from './migrate-automatic-var-injection' import { migrateBareValueUtilities } from './migrate-bare-utilities' import { migrateBgGradient } from './migrate-bg-gradient' +import { migrateCamelcaseInNamedValue } from './migrate-camelcase-in-named-value' import { migrateDropUnnecessaryDataTypes } from './migrate-drop-unnecessary-data-types' import { migrateEmptyArbitraryValues } from './migrate-handle-empty-arbitrary-values' import { migrateImportant } from './migrate-important' @@ -41,6 +42,7 @@ export const DEFAULT_MIGRATIONS: Migration[] = [ migrateImportant, migrateBgGradient, migrateSimpleLegacyClasses, + migrateCamelcaseInNamedValue, migrateLegacyClasses, migrateMaxWidthScreen, migrateThemeToVar,