Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- _Upgrade (experimental)_: Ensure legacy theme values ending in `1` (like `theme(spacing.1)`) are correctly migrated to custom properties ([#14724](https://github.com/tailwindlabs/tailwindcss/pull/14724))
- _Upgrade (experimental)_: Migrate arbitrary values to bare values for the `from-*`, `via-*`, and `to-*` utilities ([#14725](https://github.com/tailwindlabs/tailwindcss/pull/14725))
- _Upgrade (experimental)_: Ensure `layer(utilities)` is removed from `@import` to keep `@utility` top-level ([#14738](https://github.com/tailwindlabs/tailwindcss/pull/14738))
- _Upgrade (experimental)_: Ensure JS theme keys special characters are migrated correctly ([#14736](https://github.com/tailwindlabs/tailwindcss/pull/14736))
- _Upgrade (experimental)_: Don't migrate important modifiers that are actually logical negations (e.g. `let foo = !border` to `let foo = border!`) ([#14737](https://github.com/tailwindlabs/tailwindcss/pull/14737))

### Changed
Expand Down
99 changes: 99 additions & 0 deletions integrations/upgrade/js-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,57 @@ test(
sm: ['0.875rem', { lineHeight: '1.5rem' }],
base: ['1rem', { lineHeight: '2rem' }],
},
width: {
px: '1px',
auto: 'auto',
1: '0.25rem',
1.5: '0.375rem',
2: '0.5rem',
2.5: '0.625rem',
3: '0.75rem',
3.5: '0.875rem',
4: '1rem',
5: '1.25rem',
6: '1.5rem',
8: '2rem',
10: '2.5rem',
11: '2.75rem',
12: '3rem',
16: '4rem',
24: '6rem',
32: '8rem',
40: '10rem',
48: '12rem',
64: '16rem',
80: '20rem',
96: '24rem',
128: '32rem',

full: '100%',
0: '0%',
'1/2': '50%',
'1/3': 'calc(100% / 3)',
'2/3': 'calc(100% / 3 * 2)',
'1/4': '25%',
'3/4': '75%',
'1/5': '20%',
'2/5': '40%',
'3/5': '60%',
'4/5': '80%',
'1/6': 'calc(100% / 6)',
'5/6': 'calc(100% / 6 * 5)',
'1/7': 'calc(100% / 7)',
'1/10': 'calc(100% / 10)',
'3/10': 'calc(100% / 10 * 3)',
'7/10': 'calc(100% / 10 * 7)',
'9/10': 'calc(100% / 10 * 9)',
screen: '100vw',

'full-minus-80': 'calc(100% - 20rem)',
'full-minus-96': 'calc(100% - 24rem)',

'225px': '225px',
},
extend: {
colors: {
red: {
Expand Down Expand Up @@ -113,6 +164,54 @@ test(
--font-size-base: 1rem;
--font-size-base--line-height: 2rem;

--width-*: initial;
--width-0: 0%;
--width-1: 0.25rem;
--width-2: 0.5rem;
--width-3: 0.75rem;
--width-4: 1rem;
--width-5: 1.25rem;
--width-6: 1.5rem;
--width-8: 2rem;
--width-10: 2.5rem;
--width-11: 2.75rem;
--width-12: 3rem;
--width-16: 4rem;
--width-24: 6rem;
--width-32: 8rem;
--width-40: 10rem;
--width-48: 12rem;
--width-64: 16rem;
--width-80: 20rem;
--width-96: 24rem;
--width-128: 32rem;
--width-px: 1px;
--width-auto: auto;
--width-1_5: 0.375rem;
--width-2_5: 0.625rem;
--width-3_5: 0.875rem;
--width-full: 100%;
--width-1\\/2: 50%;
--width-1\\/3: calc(100% / 3);
--width-2\\/3: calc(100% / 3 * 2);
--width-1\\/4: 25%;
--width-3\\/4: 75%;
--width-1\\/5: 20%;
--width-2\\/5: 40%;
--width-3\\/5: 60%;
--width-4\\/5: 80%;
--width-1\\/6: calc(100% / 6);
--width-5\\/6: calc(100% / 6 * 5);
--width-1\\/7: calc(100% / 7);
--width-1\\/10: calc(100% / 10);
--width-3\\/10: calc(100% / 10 * 3);
--width-7\\/10: calc(100% / 10 * 7);
--width-9\\/10: calc(100% / 10 * 9);
--width-screen: 100vw;
--width-full-minus-80: calc(100% - 20rem);
--width-full-minus-96: calc(100% - 24rem);
--width-225px: 225px;

--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';
Expand Down
11 changes: 9 additions & 2 deletions packages/@tailwindcss-upgrade/src/migrate-js-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { resolveConfig, type ConfigFile } from '../../tailwindcss/src/compat/con
import type { ThemeConfig } from '../../tailwindcss/src/compat/config/types'
import { darkModePlugin } from '../../tailwindcss/src/compat/dark-mode'
import type { DesignSystem } from '../../tailwindcss/src/design-system'
import { escape } from '../../tailwindcss/src/utils/escape'
import { findStaticPlugins, type StaticPluginOptions } from './utils/extract-static-plugins'
import { info } from './utils/renderer'

Expand Down Expand Up @@ -121,10 +122,16 @@ async function migrateTheme(

if (resetNamespaces.has(key[0]) && resetNamespaces.get(key[0]) === false) {
resetNamespaces.set(key[0], true)
css += ` --${keyPathToCssProperty([key[0]])}-*: initial;\n`
let property = keyPathToCssProperty([key[0]])
if (property !== null) {
css += ` --${escape(property)}-*: initial;\n`
}
}

css += ` --${keyPathToCssProperty(key)}: ${value};\n`
let property = keyPathToCssProperty(key)
if (property !== null) {
css += ` --${escape(property)}: ${value};\n`
}
Copy link
Member

Choose a reason for hiding this comment

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

It might be wise to prepend the -- before running escape here, because certain characters need to be escaped if they are the very first character but don't need to be escaped if they are not:

image

}

if ('keyframes' in resolvedConfig.theme) {
Expand Down
13 changes: 11 additions & 2 deletions packages/tailwindcss/src/compat/apply-config-to-theme.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect, test } from 'vitest'
import { describe, expect, test } from 'vitest'
import { buildDesignSystem } from '../design-system'
import { Theme, ThemeOptions } from '../theme'
import { applyConfigToTheme } from './apply-config-to-theme'
import { applyConfigToTheme, keyPathToCssProperty } from './apply-config-to-theme'
import { resolveConfig } from './config/resolve-config'

test('config values can be merged into the theme', () => {
Expand Down Expand Up @@ -157,3 +157,12 @@ test('invalid keys are not merged into the theme', () => {

expect(entries.length).toEqual(0)
})

describe('keyPathToCssProperty', () => {
test.each([
[['width', '40', '2/5'], '--width-40-2/5'],
[['spacing', '0.5'], '--spacing-0_5'],
])('converts %s to %s', (keyPath, expected) => {
expect(`--${keyPathToCssProperty(keyPath)}`).toEqual(expected)
})
})