diff --git a/CHANGELOG.md b/CHANGELOG.md index eb5d70dd6be8..d08332a108cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,9 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Support using CSS variables as arbitrary values without `var(…)` by using parentheses instead of square brackets (e.g. `bg-(--my-color)`) ([#15020](https://github.com/tailwindlabs/tailwindcss/pull/15020)) - Add new `in-*` variant ([#15025](https://github.com/tailwindlabs/tailwindcss/pull/15025)) - Allow `addUtilities()` and `addComponents()` to work with child combinators and other complex selectors ([#15029](https://github.com/tailwindlabs/tailwindcss/pull/15029)) +- Support colors that use `` in JS configs and plugins ([#15033](https://github.com/tailwindlabs/tailwindcss/pull/15033)) - _Upgrade (experimental)_: Migrate `[&>*]` to the `*` variant ([#15022](https://github.com/tailwindlabs/tailwindcss/pull/15022)) - _Upgrade (experimental)_: Migrate `[&_*]` to the `**` variant ([#15022](https://github.com/tailwindlabs/tailwindcss/pull/15022)) - _Upgrade (experimental)_: Warn when trying to migrating a project that is not on Tailwind CSS v3 ([#15015](https://github.com/tailwindlabs/tailwindcss/pull/15015)) +- _Upgrade (experimental)_: Migrate colors that use `` in JS configs ([#15033](https://github.com/tailwindlabs/tailwindcss/pull/15033)) ### Fixed diff --git a/integrations/upgrade/js-config.test.ts b/integrations/upgrade/js-config.test.ts index 85c0150dc485..e0173bcf367c 100644 --- a/integrations/upgrade/js-config.test.ts +++ b/integrations/upgrade/js-config.test.ts @@ -30,6 +30,8 @@ test( 400: '#f87171', 500: 'red', }, + steel: 'rgb(70 130 180 / )', + smoke: 'rgba(245, 245, 245, var(--smoke-alpha, ))', }, fontSize: { xs: ['0.75rem', { lineHeight: '1rem' }], @@ -175,6 +177,9 @@ test( --color-red-500: #ef4444; --color-red-600: #dc2626; + --color-steel: rgb(70 130 180); + --color-smoke: rgba(245, 245, 245, var(--smoke-alpha, 1)); + --text-*: initial; --text-xs: 0.75rem; --text-xs--line-height: 1rem; diff --git a/packages/@tailwindcss-upgrade/src/migrate-js-config.ts b/packages/@tailwindcss-upgrade/src/migrate-js-config.ts index e17bf9601c75..a31cd9ce5fc1 100644 --- a/packages/@tailwindcss-upgrade/src/migrate-js-config.ts +++ b/packages/@tailwindcss-upgrade/src/migrate-js-config.ts @@ -115,6 +115,16 @@ async function migrateTheme( continue } + if (typeof value === 'string') { + // This is more advanced than the version in core as ideally something + // like `rgba(0 0 0 / )` becomes `rgba(0 0 0)`. Since we know + // from the `/` that it's used in an alpha channel and we can remove it. + // + // In other cases we may not know exactly how its used, so we'll just + // replace it with `1` like core does. + value = value.replace(/\s*\/\s*/, '').replace(//, '1') + } + if (key[0] === 'keyframes') { continue } diff --git a/packages/tailwindcss/src/compat/apply-config-to-theme.ts b/packages/tailwindcss/src/compat/apply-config-to-theme.ts index 64f8d0c210b3..0bdfdb242342 100644 --- a/packages/tailwindcss/src/compat/apply-config-to-theme.ts +++ b/packages/tailwindcss/src/compat/apply-config-to-theme.ts @@ -37,6 +37,10 @@ export function applyConfigToTheme( continue } + if (typeof value === 'string') { + value = value.replace(//g, '1') + } + let name = keyPathToCssProperty(path) if (!name) continue diff --git a/packages/tailwindcss/src/compat/plugin-api.test.ts b/packages/tailwindcss/src/compat/plugin-api.test.ts index a74e6cdc7c3f..51b1d9732132 100644 --- a/packages/tailwindcss/src/compat/plugin-api.test.ts +++ b/packages/tailwindcss/src/compat/plugin-api.test.ts @@ -299,6 +299,93 @@ describe('theme', async () => { " `) }) + + test('plugin theme colors can use ', async () => { + let input = css` + @tailwind utilities; + @theme { + /* This should not work */ + --color-custom-css: rgba(255 0 0 / ); + } + @plugin "my-plugin"; + ` + + let compiler = await compile(input, { + loadModule: async (id, base) => { + return { + base, + module: plugin( + function ({ addUtilities, theme }) { + addUtilities({ + '.css-percentage': { + color: theme('colors.custom-css / 50%'), + }, + '.css-fraction': { + color: theme('colors.custom-css / 0.5'), + }, + '.css-variable': { + color: theme('colors.custom-css / var(--opacity)'), + }, + '.js-percentage': { + color: theme('colors.custom-js / 50%'), + }, + '.js-fraction': { + color: theme('colors.custom-js / 0.5'), + }, + '.js-variable': { + color: theme('colors.custom-js / var(--opacity)'), + }, + }) + }, + { + theme: { + colors: { + /* This should work */ + 'custom-js': 'rgb(255 0 0 / )', + }, + }, + }, + ), + } + }, + }) + + expect( + compiler.build([ + 'bg-custom', + 'css-percentage', + 'css-fraction', + 'css-variable', + 'js-percentage', + 'js-fraction', + 'js-variable', + ]), + ).toMatchInlineSnapshot(` + ".css-fraction { + color: color-mix(in oklch, rgba(255 0 0 / ) 50%, transparent); + } + .css-percentage { + color: color-mix(in oklch, rgba(255 0 0 / ) 50%, transparent); + } + .css-variable { + color: color-mix(in oklch, rgba(255 0 0 / ) var(--opacity), transparent); + } + .js-fraction { + color: color-mix(in oklch, rgb(255 0 0 / 1) 50%, transparent); + } + .js-percentage { + color: color-mix(in oklch, rgb(255 0 0 / 1) 50%, transparent); + } + .js-variable { + color: color-mix(in oklch, rgb(255 0 0 / 1) var(--opacity), transparent); + } + :root { + --color-custom-css: rgba(255 0 0 / ); + } + " + `) + }) + test('theme value functions are resolved correctly regardless of order', async () => { let input = css` @tailwind utilities; diff --git a/packages/tailwindcss/src/compat/plugin-functions.ts b/packages/tailwindcss/src/compat/plugin-functions.ts index 5f8fc8843383..9e3917eaaf90 100644 --- a/packages/tailwindcss/src/compat/plugin-functions.ts +++ b/packages/tailwindcss/src/compat/plugin-functions.ts @@ -30,6 +30,10 @@ export function createThemeFn( let configValue = resolveValue(get(configTheme() ?? {}, keypath) ?? null) + if (typeof configValue === 'string') { + configValue = configValue.replace('', '1') + } + // Resolved to a primitive value. if (typeof cssValue !== 'object') { if (typeof options !== 'object' && options & ThemeOptions.DEFAULT) {