Skip to content

Commit

Permalink
Interpolate gradients using OKLCH by default (#14708)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
adamwathan and adamwathan authored Oct 17, 2024
1 parent 72c30d4 commit edb066e
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 43 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Add first draft of new wide-gamut color palette ([#14693](https://github.com/tailwindlabs/tailwindcss/pull/14693))
- Support linear gradient angles as bare values ([#14707](https://github.com/tailwindlabs/tailwindcss/pull/14707))
- Interpolate gradients in OKLCH by default ([#14708](https://github.com/tailwindlabs/tailwindcss/pull/14708))
- _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))

### Fixed
Expand Down
40 changes: 20 additions & 20 deletions packages/tailwindcss/src/utilities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9557,8 +9557,8 @@ test('bg', async () => {
}
.-bg-linear-45 {
--tw-gradient-position: calc(45deg * -1), ;
background-image: linear-gradient(var(--tw-gradient-stops, calc(45deg * -1)));
--tw-gradient-position: calc(45deg * -1) in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.-bg-linear-\\[1\\.3rad\\] {
Expand All @@ -9572,48 +9572,48 @@ test('bg', async () => {
}
.bg-gradient-to-b {
--tw-gradient-position: to bottom, ;
--tw-gradient-position: to bottom in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-gradient-to-bl {
--tw-gradient-position: to bottom left, ;
--tw-gradient-position: to bottom left in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-gradient-to-br {
--tw-gradient-position: to bottom right, ;
--tw-gradient-position: to bottom right in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-gradient-to-l {
--tw-gradient-position: to left, ;
--tw-gradient-position: to left in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-gradient-to-r {
--tw-gradient-position: to right, ;
--tw-gradient-position: to right in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-gradient-to-t {
--tw-gradient-position: to top, ;
--tw-gradient-position: to top in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-gradient-to-tl {
--tw-gradient-position: to top left, ;
--tw-gradient-position: to top left in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-gradient-to-tr {
--tw-gradient-position: to top right, ;
--tw-gradient-position: to top right in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-linear-45 {
--tw-gradient-position: 45deg, ;
background-image: linear-gradient(var(--tw-gradient-stops, 45deg));
--tw-gradient-position: 45deg in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-linear-\\[1\\.3rad\\] {
Expand All @@ -9632,42 +9632,42 @@ test('bg', async () => {
}
.bg-linear-to-b {
--tw-gradient-position: to bottom, ;
--tw-gradient-position: to bottom in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-linear-to-bl {
--tw-gradient-position: to bottom left, ;
--tw-gradient-position: to bottom left in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-linear-to-br {
--tw-gradient-position: to bottom right, ;
--tw-gradient-position: to bottom right in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-linear-to-l {
--tw-gradient-position: to left, ;
--tw-gradient-position: to left in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-linear-to-r {
--tw-gradient-position: to right, ;
--tw-gradient-position: to right in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-linear-to-t {
--tw-gradient-position: to top, ;
--tw-gradient-position: to top in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-linear-to-tl {
--tw-gradient-position: to top left, ;
--tw-gradient-position: to top left in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.bg-linear-to-tr {
--tw-gradient-position: to top right, ;
--tw-gradient-position: to top right in oklch, ;
background-image: linear-gradient(var(--tw-gradient-stops));
}
Expand Down
14 changes: 7 additions & 7 deletions packages/tailwindcss/src/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2516,12 +2516,12 @@ export function createUtilities(theme: Theme) {
['tl', 'top left'],
]) {
staticUtility(`bg-gradient-to-${value}`, [
['--tw-gradient-position', `to ${direction},`],
['--tw-gradient-position', `to ${direction} in oklch,`],
['background-image', `linear-gradient(var(--tw-gradient-stops))`],
])

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

return [
decl('--tw-gradient-position', `${value},`),
decl('background-image', `linear-gradient(var(--tw-gradient-stops,${value}))`),
decl('--tw-gradient-position', `${value} in oklch,`),
decl('background-image', `linear-gradient(var(--tw-gradient-stops))`),
]
}
})
Expand All @@ -2569,7 +2569,7 @@ export function createUtilities(theme: Theme) {

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

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

if (!candidate.value) {
return [
decl('--tw-gradient-position', `initial`),
decl('--tw-gradient-position', `in oklch,`),
decl('background-image', `radial-gradient(var(--tw-gradient-stops))`),
]
}
Expand Down
38 changes: 22 additions & 16 deletions packages/tailwindcss/tests/ui.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,37 +30,40 @@ test('touch action', async ({ page }) => {
for (let [classes, expected] of [
[
'bg-linear-to-r from-red',
'linear-gradient(to right, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(to right in oklch, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)',
],
[
'bg-linear-to-r via-red',
'linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgb(255, 0, 0) 50%, rgba(0, 0, 0, 0) 100%)',
'linear-gradient(to right in oklch, rgba(0, 0, 0, 0) 0%, rgb(255, 0, 0) 50%, rgba(0, 0, 0, 0) 100%)',
],
[
'bg-linear-to-r to-red',
'linear-gradient(to right in oklch, rgba(0, 0, 0, 0) 0%, rgb(255, 0, 0) 100%)',
],
['bg-linear-to-r to-red', 'linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgb(255, 0, 0) 100%)'],
[
'bg-linear-to-r from-red to-blue',
'linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
'linear-gradient(to right in oklch, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
],
[
'bg-linear-45 from-red to-blue',
'linear-gradient(45deg, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
'linear-gradient(45deg in oklch, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
],
[
'-bg-linear-45 from-red to-blue',
// Chrome reports a different (but also correct) computed value than Firefox/WebKit so we check
// for both options.
[
'linear-gradient(-45deg, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
'linear-gradient(calc(-45deg), rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
'linear-gradient(-45deg in oklch, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
'linear-gradient(calc(-45deg) in oklch, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
],
],
[
'bg-linear-to-r via-red to-blue',
'linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgb(255, 0, 0) 50%, rgb(0, 0, 255) 100%)',
'linear-gradient(to right in oklch, rgba(0, 0, 0, 0) 0%, rgb(255, 0, 0) 50%, rgb(0, 0, 255) 100%)',
],
[
'bg-linear-to-r from-red via-green to-blue',
'linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(0, 255, 0) 50%, rgb(0, 0, 255) 100%)',
'linear-gradient(to right in oklch, rgb(255, 0, 0) 0%, rgb(0, 255, 0) 50%, rgb(0, 0, 255) 100%)',
],
[
'bg-linear-[to_right,var(--color-red),var(--color-green),var(--color-blue)]',
Expand Down Expand Up @@ -88,13 +91,13 @@ test('background gradient, going from 2 to 3', async ({ page }) => {
)

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

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

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

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

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

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

expect(await getPropertyValue('#x', 'background-image')).toEqual(
'linear-gradient(to right, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
'linear-gradient(to right in oklch, rgb(255, 0, 0) 0%, rgb(0, 0, 255) 100%)',
)
})

for (let [classes, expected] of [
['bg-conic from-red', 'conic-gradient(rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)'],
['bg-conic-45 from-red', 'conic-gradient(from 45deg, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)'],
['bg-conic from-red', 'conic-gradient(in oklch, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)'],
[
'bg-conic-45 from-red',
'conic-gradient(from 45deg in oklch, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)',
],
[
'bg-conic-[from_45deg] from-red',
'conic-gradient(from 45deg, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)',
Expand All @@ -142,7 +148,7 @@ for (let [classes, expected] of [
}

for (let [classes, expected] of [
['bg-radial from-red', 'radial-gradient(rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)'],
['bg-radial from-red', 'radial-gradient(in oklch, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)'],
[
'bg-radial-[at_0%_0%] from-red',
'radial-gradient(at 0% 0%, rgb(255, 0, 0) 0%, rgba(0, 0, 0, 0) 100%)',
Expand Down

0 comments on commit edb066e

Please sign in to comment.