Skip to content

Commit edb066e

Browse files
authored
Interpolate gradients using OKLCH by default (tailwindlabs#14708)
This PR updates all of our gradient utilities to interpolate using OKLCH by default instead of sRGB. This results in a smoother transition between colors that preserves saturation throughout the gradient, rather than hitting the dreaded dull gray zone in between your color stops. Here are a few examples comparing sRGB (top) to OKLCH (bottom): <img width="736" alt="image" src="https://github.com/user-attachments/assets/57a158b6-a3a2-4eda-813e-1b596c7d4b3a"> We only apply a default interpolation mode when _not_ using arbitrary values with the gradient utility. Simplified but clear: ```css .bg-linear-to-r { background-image: linear-gradient(to right in oklch, var(--gradient-color-stops)); } .bg-linear-[to_right] { background-image: linear-gradient(to right, var(--gradient-color-stops)); } .bg-linear-[to_right_in_hsl] { background-image: linear-gradient(to right in hsl, var(--gradient-color-stops)); } ``` --------- Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
1 parent 72c30d4 commit edb066e

File tree

4 files changed

+50
-43
lines changed

4 files changed

+50
-43
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- Add first draft of new wide-gamut color palette ([#14693](https://github.com/tailwindlabs/tailwindcss/pull/14693))
1313
- Support linear gradient angles as bare values ([#14707](https://github.com/tailwindlabs/tailwindcss/pull/14707))
14+
- Interpolate gradients in OKLCH by default ([#14708](https://github.com/tailwindlabs/tailwindcss/pull/14708))
1415
- _Upgrade (experimental)_: Migrate `theme(…)` calls to `var(…)` or to the modern `theme(…)` syntax ([#14664](https://github.com/tailwindlabs/tailwindcss/pull/14664), [#14695](https://github.com/tailwindlabs/tailwindcss/pull/14695))
1516

1617
### Fixed

packages/tailwindcss/src/utilities.test.ts

+20-20
Original file line numberDiff line numberDiff line change
@@ -9557,8 +9557,8 @@ test('bg', async () => {
95579557
}
95589558
95599559
.-bg-linear-45 {
9560-
--tw-gradient-position: calc(45deg * -1), ;
9561-
background-image: linear-gradient(var(--tw-gradient-stops, calc(45deg * -1)));
9560+
--tw-gradient-position: calc(45deg * -1) in oklch, ;
9561+
background-image: linear-gradient(var(--tw-gradient-stops));
95629562
}
95639563
95649564
.-bg-linear-\\[1\\.3rad\\] {
@@ -9572,48 +9572,48 @@ test('bg', async () => {
95729572
}
95739573
95749574
.bg-gradient-to-b {
9575-
--tw-gradient-position: to bottom, ;
9575+
--tw-gradient-position: to bottom in oklch, ;
95769576
background-image: linear-gradient(var(--tw-gradient-stops));
95779577
}
95789578
95799579
.bg-gradient-to-bl {
9580-
--tw-gradient-position: to bottom left, ;
9580+
--tw-gradient-position: to bottom left in oklch, ;
95819581
background-image: linear-gradient(var(--tw-gradient-stops));
95829582
}
95839583
95849584
.bg-gradient-to-br {
9585-
--tw-gradient-position: to bottom right, ;
9585+
--tw-gradient-position: to bottom right in oklch, ;
95869586
background-image: linear-gradient(var(--tw-gradient-stops));
95879587
}
95889588
95899589
.bg-gradient-to-l {
9590-
--tw-gradient-position: to left, ;
9590+
--tw-gradient-position: to left in oklch, ;
95919591
background-image: linear-gradient(var(--tw-gradient-stops));
95929592
}
95939593
95949594
.bg-gradient-to-r {
9595-
--tw-gradient-position: to right, ;
9595+
--tw-gradient-position: to right in oklch, ;
95969596
background-image: linear-gradient(var(--tw-gradient-stops));
95979597
}
95989598
95999599
.bg-gradient-to-t {
9600-
--tw-gradient-position: to top, ;
9600+
--tw-gradient-position: to top in oklch, ;
96019601
background-image: linear-gradient(var(--tw-gradient-stops));
96029602
}
96039603
96049604
.bg-gradient-to-tl {
9605-
--tw-gradient-position: to top left, ;
9605+
--tw-gradient-position: to top left in oklch, ;
96069606
background-image: linear-gradient(var(--tw-gradient-stops));
96079607
}
96089608
96099609
.bg-gradient-to-tr {
9610-
--tw-gradient-position: to top right, ;
9610+
--tw-gradient-position: to top right in oklch, ;
96119611
background-image: linear-gradient(var(--tw-gradient-stops));
96129612
}
96139613
96149614
.bg-linear-45 {
9615-
--tw-gradient-position: 45deg, ;
9616-
background-image: linear-gradient(var(--tw-gradient-stops, 45deg));
9615+
--tw-gradient-position: 45deg in oklch, ;
9616+
background-image: linear-gradient(var(--tw-gradient-stops));
96179617
}
96189618
96199619
.bg-linear-\\[1\\.3rad\\] {
@@ -9632,42 +9632,42 @@ test('bg', async () => {
96329632
}
96339633
96349634
.bg-linear-to-b {
9635-
--tw-gradient-position: to bottom, ;
9635+
--tw-gradient-position: to bottom in oklch, ;
96369636
background-image: linear-gradient(var(--tw-gradient-stops));
96379637
}
96389638
96399639
.bg-linear-to-bl {
9640-
--tw-gradient-position: to bottom left, ;
9640+
--tw-gradient-position: to bottom left in oklch, ;
96419641
background-image: linear-gradient(var(--tw-gradient-stops));
96429642
}
96439643
96449644
.bg-linear-to-br {
9645-
--tw-gradient-position: to bottom right, ;
9645+
--tw-gradient-position: to bottom right in oklch, ;
96469646
background-image: linear-gradient(var(--tw-gradient-stops));
96479647
}
96489648
96499649
.bg-linear-to-l {
9650-
--tw-gradient-position: to left, ;
9650+
--tw-gradient-position: to left in oklch, ;
96519651
background-image: linear-gradient(var(--tw-gradient-stops));
96529652
}
96539653
96549654
.bg-linear-to-r {
9655-
--tw-gradient-position: to right, ;
9655+
--tw-gradient-position: to right in oklch, ;
96569656
background-image: linear-gradient(var(--tw-gradient-stops));
96579657
}
96589658
96599659
.bg-linear-to-t {
9660-
--tw-gradient-position: to top, ;
9660+
--tw-gradient-position: to top in oklch, ;
96619661
background-image: linear-gradient(var(--tw-gradient-stops));
96629662
}
96639663
96649664
.bg-linear-to-tl {
9665-
--tw-gradient-position: to top left, ;
9665+
--tw-gradient-position: to top left in oklch, ;
96669666
background-image: linear-gradient(var(--tw-gradient-stops));
96679667
}
96689668
96699669
.bg-linear-to-tr {
9670-
--tw-gradient-position: to top right, ;
9670+
--tw-gradient-position: to top right in oklch, ;
96719671
background-image: linear-gradient(var(--tw-gradient-stops));
96729672
}
96739673

packages/tailwindcss/src/utilities.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -2516,12 +2516,12 @@ export function createUtilities(theme: Theme) {
25162516
['tl', 'top left'],
25172517
]) {
25182518
staticUtility(`bg-gradient-to-${value}`, [
2519-
['--tw-gradient-position', `to ${direction},`],
2519+
['--tw-gradient-position', `to ${direction} in oklch,`],
25202520
['background-image', `linear-gradient(var(--tw-gradient-stops))`],
25212521
])
25222522

25232523
staticUtility(`bg-linear-to-${value}`, [
2524-
['--tw-gradient-position', `to ${direction},`],
2524+
['--tw-gradient-position', `to ${direction} in oklch,`],
25252525
['background-image', `linear-gradient(var(--tw-gradient-stops))`],
25262526
])
25272527
}
@@ -2558,8 +2558,8 @@ export function createUtilities(theme: Theme) {
25582558
value = withNegative(`${value}deg`, candidate)
25592559

25602560
return [
2561-
decl('--tw-gradient-position', `${value},`),
2562-
decl('background-image', `linear-gradient(var(--tw-gradient-stops,${value}))`),
2561+
decl('--tw-gradient-position', `${value} in oklch,`),
2562+
decl('background-image', `linear-gradient(var(--tw-gradient-stops))`),
25632563
]
25642564
}
25652565
})
@@ -2569,7 +2569,7 @@ export function createUtilities(theme: Theme) {
25692569

25702570
if (!candidate.value) {
25712571
return [
2572-
decl('--tw-gradient-position', `initial`),
2572+
decl('--tw-gradient-position', `in oklch,`),
25732573
decl('background-image', `conic-gradient(var(--tw-gradient-stops))`),
25742574
]
25752575
}
@@ -2587,7 +2587,7 @@ export function createUtilities(theme: Theme) {
25872587
value = withNegative(`${value}deg`, candidate)
25882588

25892589
return [
2590-
decl('--tw-gradient-position', `from ${value},`),
2590+
decl('--tw-gradient-position', `from ${value} in oklch,`),
25912591
decl('background-image', `conic-gradient(var(--tw-gradient-stops))`),
25922592
]
25932593
}
@@ -2598,7 +2598,7 @@ export function createUtilities(theme: Theme) {
25982598

25992599
if (!candidate.value) {
26002600
return [
2601-
decl('--tw-gradient-position', `initial`),
2601+
decl('--tw-gradient-position', `in oklch,`),
26022602
decl('background-image', `radial-gradient(var(--tw-gradient-stops))`),
26032603
]
26042604
}

packages/tailwindcss/tests/ui.spec.ts

+22-16
Original file line numberDiff line numberDiff line change
@@ -30,37 +30,40 @@ test('touch action', async ({ page }) => {
3030
for (let [classes, expected] of [
3131
[
3232
'bg-linear-to-r from-red',
33-
'linear-gradient(to right, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)',
33+
'linear-gradient(to right in oklch, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)',
3434
],
3535
[
3636
'bg-linear-to-r via-red',
37-
'linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgb(255, 0, 0) 50%, rgba(0, 0, 0, 0) 100%)',
37+
'linear-gradient(to right in oklch, rgba(0, 0, 0, 0) 0%, rgb(255, 0, 0) 50%, rgba(0, 0, 0, 0) 100%)',
38+
],
39+
[
40+
'bg-linear-to-r to-red',
41+
'linear-gradient(to right in oklch, rgba(0, 0, 0, 0) 0%, rgb(255, 0, 0) 100%)',
3842
],
39-
['bg-linear-to-r to-red', 'linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgb(255, 0, 0) 100%)'],
4043
[
4144
'bg-linear-to-r from-red to-blue',
42-
'linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
45+
'linear-gradient(to right in oklch, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
4346
],
4447
[
4548
'bg-linear-45 from-red to-blue',
46-
'linear-gradient(45deg, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
49+
'linear-gradient(45deg in oklch, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
4750
],
4851
[
4952
'-bg-linear-45 from-red to-blue',
5053
// Chrome reports a different (but also correct) computed value than Firefox/WebKit so we check
5154
// for both options.
5255
[
53-
'linear-gradient(-45deg, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
54-
'linear-gradient(calc(-45deg), rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
56+
'linear-gradient(-45deg in oklch, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
57+
'linear-gradient(calc(-45deg) in oklch, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
5558
],
5659
],
5760
[
5861
'bg-linear-to-r via-red to-blue',
59-
'linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgb(255, 0, 0) 50%, rgb(0, 0, 255) 100%)',
62+
'linear-gradient(to right in oklch, rgba(0, 0, 0, 0) 0%, rgb(255, 0, 0) 50%, rgb(0, 0, 255) 100%)',
6063
],
6164
[
6265
'bg-linear-to-r from-red via-green to-blue',
63-
'linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(0, 255, 0) 50%, rgb(0, 0, 255) 100%)',
66+
'linear-gradient(to right in oklch, rgb(255, 0, 0) 0%, rgb(0, 255, 0) 50%, rgb(0, 0, 255) 100%)',
6467
],
6568
[
6669
'bg-linear-[to_right,var(--color-red),var(--color-green),var(--color-blue)]',
@@ -88,13 +91,13 @@ test('background gradient, going from 2 to 3', async ({ page }) => {
8891
)
8992

9093
expect(await getPropertyValue('#x', 'background-image')).toEqual(
91-
'linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
94+
'linear-gradient(to right in oklch, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
9295
)
9396

9497
await page.locator('#x').hover()
9598

9699
expect(await getPropertyValue('#x', 'background-image')).toEqual(
97-
'linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(0, 255, 0) 50%, rgb(0, 0, 255) 100%)',
100+
'linear-gradient(to right in oklch, rgb(255, 0, 0) 0%, rgb(0, 255, 0) 50%, rgb(0, 0, 255) 100%)',
98101
)
99102
})
100103

@@ -109,19 +112,22 @@ test('background gradient, going from 3 to 2', async ({ page }) => {
109112
)
110113

111114
expect(await getPropertyValue('#x', 'background-image')).toEqual(
112-
'linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(0, 255, 0) 50%, rgb(0, 0, 255) 100%)',
115+
'linear-gradient(to right in oklch, rgb(255, 0, 0) 0%, rgb(0, 255, 0) 50%, rgb(0, 0, 255) 100%)',
113116
)
114117

115118
await page.locator('#x').hover()
116119

117120
expect(await getPropertyValue('#x', 'background-image')).toEqual(
118-
'linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
121+
'linear-gradient(to right in oklch, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
119122
)
120123
})
121124

122125
for (let [classes, expected] of [
123-
['bg-conic from-red', 'conic-gradient(rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)'],
124-
['bg-conic-45 from-red', 'conic-gradient(from 45deg, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)'],
126+
['bg-conic from-red', 'conic-gradient(in oklch, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)'],
127+
[
128+
'bg-conic-45 from-red',
129+
'conic-gradient(from 45deg in oklch, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)',
130+
],
125131
[
126132
'bg-conic-[from_45deg] from-red',
127133
'conic-gradient(from 45deg, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)',
@@ -142,7 +148,7 @@ for (let [classes, expected] of [
142148
}
143149

144150
for (let [classes, expected] of [
145-
['bg-radial from-red', 'radial-gradient(rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)'],
151+
['bg-radial from-red', 'radial-gradient(in oklch, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)'],
146152
[
147153
'bg-radial-[at_0%_0%] from-red',
148154
'radial-gradient(at 0% 0%, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)',

0 commit comments

Comments
 (0)