Skip to content

Commit 2a6cd94

Browse files
Bring back named opacity support for color opacity modifiers (#15009)
This PR reverts #14278 to bring back support for using named opacity values in color opacity modifiers: ```css @theme { --opacity-myOpacity: 50%; } ``` ```html <div class="bg-red-500/myOpacity"></div> ``` We briefly discuss to restructure the code so that we avoid adding a `theme` argument to the call sites but I do still prefer the current approach for the following reasons: The way to avoid this is to a) put something in either the `Theme` class scope, where it feels grossly out of place, or b) put it into the shared closure in the utilities file which is already very large and hard to reason. Furthermore, there's a second call site in the compile function where we would need to duplicate the namespace lookup. Every caller of the current `asColor` value already has access to the `Theme` so passing that as an argument seems like the least intrusive way. ## Test Plan Brought back the unit tests but I also tested it with the Vite extension: <img width="744" alt="Screenshot 2024-11-15 at 11 15 05" src="https://github.com/user-attachments/assets/63923b80-767e-4104-b7eb-f71fc815b51e"> --------- Co-authored-by: Adam Wathan <[email protected]>
1 parent 953ecd2 commit 2a6cd94

File tree

4 files changed

+44
-19
lines changed

4 files changed

+44
-19
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
- Nothing yet!
10+
### Changed
11+
12+
- Bring back support for color opacity modifiers to read from `--opacity-*` theme values ([#14278](https://github.com/tailwindlabs/tailwindcss/pull/14278))
1113

1214
## [4.0.0-alpha.34] - 2024-11-14
1315

packages/tailwindcss/src/compile.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ function compileBaseUtility(candidate: Candidate, designSystem: DesignSystem) {
263263
// Assumption: If an arbitrary property has a modifier, then we assume it
264264
// is an opacity modifier.
265265
if (candidate.modifier) {
266-
value = asColor(value, candidate.modifier)
266+
value = asColor(value, candidate.modifier, designSystem.theme)
267267
}
268268

269269
if (value === null) return []

packages/tailwindcss/src/utilities.test.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10998,7 +10998,19 @@ test('bg', async () => {
1099810998
`,
1099910999
['bg-current/half', 'bg-current/custom', '[color:red]/half'],
1100011000
),
11001-
).toEqual('')
11001+
).toMatchInlineSnapshot(`
11002+
".bg-current\\/custom {
11003+
background-color: color-mix(in oklch, currentColor var(--opacity-custom), transparent);
11004+
}
11005+
11006+
.bg-current\\/half {
11007+
background-color: color-mix(in oklch, currentColor var(--opacity-half), transparent);
11008+
}
11009+
11010+
.\\[color\\:red\\]\\/half {
11011+
color: color-mix(in oklch, red var(--opacity-half), transparent);
11012+
}"
11013+
`)
1100211014
})
1100311015

1100411016
test('from', async () => {

packages/tailwindcss/src/utilities.ts

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,24 @@ export function withAlpha(value: string, alpha: string): string {
124124
/**
125125
* Resolve a color value + optional opacity modifier to a final color.
126126
*/
127-
export function asColor(value: string, modifier: CandidateModifier | null): string | null {
127+
export function asColor(
128+
value: string,
129+
modifier: CandidateModifier | null,
130+
theme: Theme,
131+
): string | null {
128132
if (!modifier) return value
129133

130134
if (modifier.kind === 'arbitrary') {
131135
return withAlpha(value, modifier.value)
132136
}
133137

138+
// Check if the modifier exists in the `opacity` theme configuration and use
139+
// that value if so.
140+
let alpha = theme.resolve(modifier.value, ['--opacity'])
141+
if (alpha) {
142+
return withAlpha(value, alpha)
143+
}
144+
134145
if (!isValidOpacityValue(modifier.value)) {
135146
return null
136147
}
@@ -182,7 +193,7 @@ function resolveThemeColor<T extends ThemeKey>(
182193
}
183194
}
184195

185-
return value ? asColor(value, candidate.modifier) : null
196+
return value ? asColor(value, candidate.modifier, theme) : null
186197
}
187198

188199
export function createUtilities(theme: Theme) {
@@ -340,7 +351,7 @@ export function createUtilities(theme: Theme) {
340351
value = candidate.value.value
341352

342353
// Apply an opacity modifier to the value if appropriate.
343-
value = asColor(value, candidate.modifier)
354+
value = asColor(value, candidate.modifier, theme)
344355
} else {
345356
value = resolveThemeColor(candidate, theme, desc.themeKeys)
346357
}
@@ -2048,7 +2059,7 @@ export function createUtilities(theme: Theme) {
20482059
return [borderProperties(), ...decls]
20492060
}
20502061
default: {
2051-
value = asColor(value, candidate.modifier)
2062+
value = asColor(value, candidate.modifier, theme)
20522063
if (value === null) return
20532064

20542065
return desc.color(value)
@@ -2510,7 +2521,7 @@ export function createUtilities(theme: Theme) {
25102521
return [decl('background-image', value)]
25112522
}
25122523
default: {
2513-
value = asColor(value, candidate.modifier)
2524+
value = asColor(value, candidate.modifier, theme)
25142525
if (value === null) return
25152526

25162527
return [decl('background-color', value)]
@@ -2584,7 +2595,7 @@ export function createUtilities(theme: Theme) {
25842595
return desc.position(value)
25852596
}
25862597
default: {
2587-
value = asColor(value, candidate.modifier)
2598+
value = asColor(value, candidate.modifier, theme)
25882599
if (value === null) return
25892600

25902601
return desc.color(value)
@@ -2721,7 +2732,7 @@ export function createUtilities(theme: Theme) {
27212732
if (!candidate.value) return
27222733

27232734
if (candidate.value.kind === 'arbitrary') {
2724-
let value = asColor(candidate.value.value, candidate.modifier)
2735+
let value = asColor(candidate.value.value, candidate.modifier, theme)
27252736
if (value === null) return
27262737
return [decl('fill', value)]
27272738
}
@@ -2758,7 +2769,7 @@ export function createUtilities(theme: Theme) {
27582769
return [decl('stroke-width', value)]
27592770
}
27602771
default: {
2761-
value = asColor(candidate.value.value, candidate.modifier)
2772+
value = asColor(candidate.value.value, candidate.modifier, theme)
27622773
if (value === null) return
27632774

27642775
return [decl('stroke', value)]
@@ -3002,7 +3013,7 @@ export function createUtilities(theme: Theme) {
30023013
return [decl('text-decoration-thickness', value)]
30033014
}
30043015
default: {
3005-
value = asColor(value, candidate.modifier)
3016+
value = asColor(value, candidate.modifier, theme)
30063017
if (value === null) return
30073018

30083019
return [decl('text-decoration-color', value)]
@@ -3880,7 +3891,7 @@ export function createUtilities(theme: Theme) {
38803891
]
38813892
}
38823893
default: {
3883-
value = asColor(value, candidate.modifier)
3894+
value = asColor(value, candidate.modifier, theme)
38843895
if (value === null) return
38853896

38863897
return [decl('outline-color', value)]
@@ -4017,7 +4028,7 @@ export function createUtilities(theme: Theme) {
40174028
return [decl('font-size', value)]
40184029
}
40194030
default: {
4020-
value = asColor(value, candidate.modifier)
4031+
value = asColor(value, candidate.modifier, theme)
40214032
if (value === null) return
40224033

40234034
return [decl('color', value)]
@@ -4151,7 +4162,7 @@ export function createUtilities(theme: Theme) {
41514162

41524163
switch (type) {
41534164
case 'color': {
4154-
value = asColor(value, candidate.modifier)
4165+
value = asColor(value, candidate.modifier, theme)
41554166
if (value === null) return
41564167

41574168
return [boxShadowProperties(), decl('--tw-shadow-color', value)]
@@ -4243,7 +4254,7 @@ export function createUtilities(theme: Theme) {
42434254

42444255
switch (type) {
42454256
case 'color': {
4246-
value = asColor(value, candidate.modifier)
4257+
value = asColor(value, candidate.modifier, theme)
42474258
if (value === null) return
42484259

42494260
return [boxShadowProperties(), decl('--tw-inset-shadow-color', value)]
@@ -4341,7 +4352,7 @@ export function createUtilities(theme: Theme) {
43414352
]
43424353
}
43434354
default: {
4344-
value = asColor(value, candidate.modifier)
4355+
value = asColor(value, candidate.modifier, theme)
43454356
if (value === null) return
43464357

43474358
return [decl('--tw-ring-color', value)]
@@ -4414,7 +4425,7 @@ export function createUtilities(theme: Theme) {
44144425
]
44154426
}
44164427
default: {
4417-
value = asColor(value, candidate.modifier)
4428+
value = asColor(value, candidate.modifier, theme)
44184429
if (value === null) return
44194430

44204431
return [decl('--tw-inset-ring-color', value)]
@@ -4478,7 +4489,7 @@ export function createUtilities(theme: Theme) {
44784489
]
44794490
}
44804491
default: {
4481-
value = asColor(value, candidate.modifier)
4492+
value = asColor(value, candidate.modifier, theme)
44824493
if (value === null) return
44834494

44844495
return [decl('--tw-ring-offset-color', value)]

0 commit comments

Comments
 (0)