Skip to content

Commit b73c746

Browse files
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.
1 parent b3d1cbb commit b73c746

File tree

6 files changed

+112
-0
lines changed

6 files changed

+112
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
- 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))
1414
- Add new `in-*` variant ([#15025](https://github.com/tailwindlabs/tailwindcss/pull/15025))
1515
- Allow `addUtilities()` and `addComponents()` to work with child combinators and other complex selectors ([#15029](https://github.com/tailwindlabs/tailwindcss/pull/15029))
16+
- Support colors that use `<alpha-value>` in JS configs and plugins ([#15033](https://github.com/tailwindlabs/tailwindcss/pull/15033))
1617
- _Upgrade (experimental)_: Migrate `[&>*]` to the `*` variant ([#15022](https://github.com/tailwindlabs/tailwindcss/pull/15022))
1718
- _Upgrade (experimental)_: Migrate `[&_*]` to the `**` variant ([#15022](https://github.com/tailwindlabs/tailwindcss/pull/15022))
1819
- _Upgrade (experimental)_: Warn when trying to migrating a project that is not on Tailwind CSS v3 ([#15015](https://github.com/tailwindlabs/tailwindcss/pull/15015))
20+
- _Upgrade (experimental)_: Migrate colors that use `<alpha-value>` in JS configs ([#15033](https://github.com/tailwindlabs/tailwindcss/pull/15033))
1921

2022
### Fixed
2123

integrations/upgrade/js-config.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ test(
3030
400: '#f87171',
3131
500: 'red',
3232
},
33+
steel: 'rgb(70 130 180 / <alpha-value>)',
34+
smoke: 'rgba(245, 245, 245, var(--smoke-alpha, <alpha-value>))',
3335
},
3436
fontSize: {
3537
xs: ['0.75rem', { lineHeight: '1rem' }],
@@ -175,6 +177,9 @@ test(
175177
--color-red-500: #ef4444;
176178
--color-red-600: #dc2626;
177179
180+
--color-steel: rgb(70 130 180);
181+
--color-smoke: rgba(245, 245, 245, var(--smoke-alpha, 1));
182+
178183
--text-*: initial;
179184
--text-xs: 0.75rem;
180185
--text-xs--line-height: 1rem;

packages/@tailwindcss-upgrade/src/migrate-js-config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,16 @@ async function migrateTheme(
115115
continue
116116
}
117117

118+
if (typeof value === 'string') {
119+
// This is more advanced than the version in core as ideally something
120+
// like `rgba(0 0 0 / <alpha-value>)` becomes `rgba(0 0 0)`. Since we know
121+
// from the `/` that it's used in an alpha channel and we can remove it.
122+
//
123+
// In other cases we may not know exactly how its used, so we'll just
124+
// replace it with `1` like core does.
125+
value = value.replace(/\s*\/\s*<alpha-value>/, '').replace(/<alpha-value>/, '1')
126+
}
127+
118128
if (key[0] === 'keyframes') {
119129
continue
120130
}

packages/tailwindcss/src/compat/apply-config-to-theme.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ export function applyConfigToTheme(
3737
continue
3838
}
3939

40+
if (typeof value === 'string') {
41+
value = value.replace(/<alpha-value>/g, '1')
42+
}
43+
4044
let name = keyPathToCssProperty(path)
4145
if (!name) continue
4246

packages/tailwindcss/src/compat/plugin-api.test.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,93 @@ describe('theme', async () => {
299299
"
300300
`)
301301
})
302+
303+
test('plugin theme colors can use <alpha-value>', async () => {
304+
let input = css`
305+
@tailwind utilities;
306+
@theme {
307+
/* This should not work */
308+
--color-custom-css: rgba(255 0 0 / <alpha-value>);
309+
}
310+
@plugin "my-plugin";
311+
`
312+
313+
let compiler = await compile(input, {
314+
loadModule: async (id, base) => {
315+
return {
316+
base,
317+
module: plugin(
318+
function ({ addUtilities, theme }) {
319+
addUtilities({
320+
'.css-percentage': {
321+
color: theme('colors.custom-css / 50%'),
322+
},
323+
'.css-fraction': {
324+
color: theme('colors.custom-css / 0.5'),
325+
},
326+
'.css-variable': {
327+
color: theme('colors.custom-css / var(--opacity)'),
328+
},
329+
'.js-percentage': {
330+
color: theme('colors.custom-js / 50%'),
331+
},
332+
'.js-fraction': {
333+
color: theme('colors.custom-js / 0.5'),
334+
},
335+
'.js-variable': {
336+
color: theme('colors.custom-js / var(--opacity)'),
337+
},
338+
})
339+
},
340+
{
341+
theme: {
342+
colors: {
343+
/* This should work */
344+
'custom-js': 'rgb(255 0 0 / <alpha-value>)',
345+
},
346+
},
347+
},
348+
),
349+
}
350+
},
351+
})
352+
353+
expect(
354+
compiler.build([
355+
'bg-custom',
356+
'css-percentage',
357+
'css-fraction',
358+
'css-variable',
359+
'js-percentage',
360+
'js-fraction',
361+
'js-variable',
362+
]),
363+
).toMatchInlineSnapshot(`
364+
".css-fraction {
365+
color: color-mix(in oklch, rgba(255 0 0 / <alpha-value>) 50%, transparent);
366+
}
367+
.css-percentage {
368+
color: color-mix(in oklch, rgba(255 0 0 / <alpha-value>) 50%, transparent);
369+
}
370+
.css-variable {
371+
color: color-mix(in oklch, rgba(255 0 0 / <alpha-value>) var(--opacity), transparent);
372+
}
373+
.js-fraction {
374+
color: color-mix(in oklch, rgb(255 0 0 / 1) 50%, transparent);
375+
}
376+
.js-percentage {
377+
color: color-mix(in oklch, rgb(255 0 0 / 1) 50%, transparent);
378+
}
379+
.js-variable {
380+
color: color-mix(in oklch, rgb(255 0 0 / 1) var(--opacity), transparent);
381+
}
382+
:root {
383+
--color-custom-css: rgba(255 0 0 / <alpha-value>);
384+
}
385+
"
386+
`)
387+
})
388+
302389
test('theme value functions are resolved correctly regardless of order', async () => {
303390
let input = css`
304391
@tailwind utilities;

packages/tailwindcss/src/compat/plugin-functions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ export function createThemeFn(
3030

3131
let configValue = resolveValue(get(configTheme() ?? {}, keypath) ?? null)
3232

33+
if (typeof configValue === 'string') {
34+
configValue = configValue.replace('<alpha-value>', '1')
35+
}
36+
3337
// Resolved to a primitive value.
3438
if (typeof cssValue !== 'object') {
3539
if (typeof options !== 'object' && options & ThemeOptions.DEFAULT) {

0 commit comments

Comments
 (0)