Implement compat for <alpha-value> from v3 (#15033)

This implements backwards compatibility for colors that use the old
`<alpha-value>` feature from v3. We can do this by replacing
`<alpha-value>` with `1` because we use `color-mix` to actually apply
opacity modifiers in v4.
This commit is contained in:
Jordan Pittman 2024-11-19 10:01:45 -05:00 • committed by GitHub
parent b3d1cbb72f
commit b73c746e6e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 112 additions and 0 deletions

View file

@ -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 `<alpha-value>` 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 `<alpha-value>` in JS configs ([#15033](https://github.com/tailwindlabs/tailwindcss/pull/15033))
### Fixed

View file

@ -30,6 +30,8 @@ test(
400: '#f87171',
500: 'red',
},
steel: 'rgb(70 130 180 / <alpha-value>)',
smoke: 'rgba(245, 245, 245, var(--smoke-alpha, <alpha-value>))',
},
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;

View file

@ -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 / <alpha-value>)` 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*<alpha-value>/, '').replace(/<alpha-value>/, '1')
}
if (key[0] === 'keyframes') {
continue
}

View file

@ -37,6 +37,10 @@ export function applyConfigToTheme(
continue
}
if (typeof value === 'string') {
value = value.replace(/<alpha-value>/g, '1')
}
let name = keyPathToCssProperty(path)
if (!name) continue

View file

@ -299,6 +299,93 @@ describe('theme', async () => {
"
`)
})
test('plugin theme colors can use <alpha-value>', async () => {
let input = css`
@tailwind utilities;
@theme {
/* This should not work */
--color-custom-css: rgba(255 0 0 / <alpha-value>);
}
@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 / <alpha-value>)',
},
},
},
),
}
},
})
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 / <alpha-value>) 50%, transparent);
}
.css-percentage {
color: color-mix(in oklch, rgba(255 0 0 / <alpha-value>) 50%, transparent);
}
.css-variable {
color: color-mix(in oklch, rgba(255 0 0 / <alpha-value>) 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 / <alpha-value>);
}
"
`)
})
test('theme value functions are resolved correctly regardless of order', async () => {
let input = css`
@tailwind utilities;

View file

@ -30,6 +30,10 @@ export function createThemeFn(
let configValue = resolveValue(get(configTheme() ?? {}, keypath) ?? null)
if (typeof configValue === 'string') {
configValue = configValue.replace('<alpha-value>', '1')
}
// Resolved to a primitive value.
if (typeof cssValue !== 'object') {
if (typeof options !== 'object' && options & ThemeOptions.DEFAULT) {