Skip to content

Commit 315e3a2

Browse files
authored
Allow for custom properties in rgb, rgba, hsl and hsla colors (#7933)
* allow for custom properties in `rgb`, `rgba`, `hsl` and `hsla` colors * update changelog * add more `parseColor` test cases
1 parent f3a629a commit 315e3a2

File tree

4 files changed

+122
-36
lines changed

4 files changed

+122
-36
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
- Fix extraction from template literal/function with array ([#7481](https://github.com/tailwindlabs/tailwindcss/pull/7481))
2121
- Don't output unparsable arbitrary values ([#7789](https://github.com/tailwindlabs/tailwindcss/pull/7789))
2222
- Fix generation of `div:not(.foo)` if `.foo` is never defined ([#7815](https://github.com/tailwindlabs/tailwindcss/pull/7815))
23+
- Allow for custom properties in `rgb`, `rgba`, `hsl` and `hsla` colors ([#7933](https://github.com/tailwindlabs/tailwindcss/pull/7933))
2324

2425
### Changed
2526

src/util/color.js

+7-5
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ import namedColors from 'color-name'
22

33
let HEX = /^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i
44
let SHORT_HEX = /^#([a-f\d])([a-f\d])([a-f\d])([a-f\d])?$/i
5-
let VALUE = `(?:\\d+|\\d*\\.\\d+)%?`
6-
let SEP = `(?:\\s*,\\s*|\\s+)`
7-
let ALPHA_SEP = `\\s*[,/]\\s*`
5+
let VALUE = /(?:\d+|\d*\.\d+)%?/
6+
let SEP = /(?:\s*,\s*|\s+)/
7+
let ALPHA_SEP = /\s*[,/]\s*/
8+
let CUSTOM_PROPERTY = /var\(--(?:[^ )]*?)\)/
9+
810
let RGB = new RegExp(
9-
`^rgba?\\(\\s*(${VALUE})${SEP}(${VALUE})${SEP}(${VALUE})(?:${ALPHA_SEP}(${VALUE}))?\\s*\\)$`
11+
`^rgba?\\(\\s*(${VALUE.source}|${CUSTOM_PROPERTY.source})${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source})${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source})(?:${ALPHA_SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?\\s*\\)$`
1012
)
1113
let HSL = new RegExp(
12-
`^hsla?\\(\\s*((?:${VALUE})(?:deg|rad|grad|turn)?)${SEP}(${VALUE})${SEP}(${VALUE})(?:${ALPHA_SEP}(${VALUE}))?\\s*\\)$`
14+
`^hsla?\\(\\s*((?:${VALUE.source})(?:deg|rad|grad|turn)?|${CUSTOM_PROPERTY.source})${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source})${SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source})(?:${ALPHA_SEP.source}(${VALUE.source}|${CUSTOM_PROPERTY.source}))?\\s*\\)$`
1315
)
1416

1517
export function parseColor(value) {

tests/color-opacity-modifiers.test.js

+59
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,62 @@ test('utilities that support any type are supported', async () => {
178178
`)
179179
})
180180
})
181+
182+
test('opacity modifier in combination with partial custom properties', async () => {
183+
let config = {
184+
content: [
185+
{
186+
raw: html`
187+
<div class="bg-[hsl(var(--foo),50%,50%)]"></div>
188+
<div class="bg-[hsl(123,var(--foo),50%)]"></div>
189+
<div class="bg-[hsl(123,50%,var(--foo))]"></div>
190+
<div class="bg-[hsl(var(--foo),50%,50%)]/50"></div>
191+
<div class="bg-[hsl(123,var(--foo),50%)]/50"></div>
192+
<div class="bg-[hsl(123,50%,var(--foo))]/50"></div>
193+
<div class="bg-[hsl(var(--foo),var(--bar),var(--baz))]/50"></div>
194+
`,
195+
},
196+
],
197+
corePlugins: { preflight: false },
198+
plugins: [],
199+
}
200+
201+
let input = css`
202+
@tailwind utilities;
203+
`
204+
205+
return run(input, config).then((result) => {
206+
expect(result.css).toMatchFormattedCss(css`
207+
.bg-\[hsl\(var\(--foo\)\2c 50\%\2c 50\%\)\] {
208+
--tw-bg-opacity: 1;
209+
background-color: hsl(var(--foo) 50% 50% / var(--tw-bg-opacity));
210+
}
211+
212+
.bg-\[hsl\(123\2c var\(--foo\)\2c 50\%\)\] {
213+
--tw-bg-opacity: 1;
214+
background-color: hsl(123 var(--foo) 50% / var(--tw-bg-opacity));
215+
}
216+
217+
.bg-\[hsl\(123\2c 50\%\2c var\(--foo\)\)\] {
218+
--tw-bg-opacity: 1;
219+
background-color: hsl(123 50% var(--foo) / var(--tw-bg-opacity));
220+
}
221+
222+
.bg-\[hsl\(var\(--foo\)\2c 50\%\2c 50\%\)\]\/50 {
223+
background-color: hsl(var(--foo) 50% 50% / 0.5);
224+
}
225+
226+
.bg-\[hsl\(123\2c var\(--foo\)\2c 50\%\)\]\/50 {
227+
background-color: hsl(123 var(--foo) 50% / 0.5);
228+
}
229+
230+
.bg-\[hsl\(123\2c 50\%\2c var\(--foo\)\)\]\/50 {
231+
background-color: hsl(123 50% var(--foo) / 0.5);
232+
}
233+
234+
.bg-\[hsl\(var\(--foo\)\2c var\(--bar\)\2c var\(--baz\)\)\]\/50 {
235+
background-color: hsl(var(--foo) var(--bar) var(--baz) / 0.5);
236+
}
237+
`)
238+
})
239+
})

tests/color.test.js

+55-31
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,61 @@ import { parseColor, formatColor } from '../src/util/color'
22

33
describe('parseColor', () => {
44
it.each`
5-
color | output
6-
${'black'} | ${{ mode: 'rgb', color: ['0', '0', '0'], alpha: undefined }}
7-
${'#0088cc'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: undefined }}
8-
${'#08c'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: undefined }}
9-
${'#0088cc99'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: '0.6' }}
10-
${'#08c9'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: '0.6' }}
11-
${'rgb(0, 30, 60)'} | ${{ mode: 'rgb', color: ['0', '30', '60'], alpha: undefined }}
12-
${'rgba(0, 30, 60, 0.5)'} | ${{ mode: 'rgb', color: ['0', '30', '60'], alpha: '0.5' }}
13-
${'rgb(0 30 60)'} | ${{ mode: 'rgb', color: ['0', '30', '60'], alpha: undefined }}
14-
${'rgb(0 30 60 / 0.5)'} | ${{ mode: 'rgb', color: ['0', '30', '60'], alpha: '0.5' }}
15-
${'hsl(0, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0', '30%', '60%'], alpha: undefined }}
16-
${'hsl(0deg, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0deg', '30%', '60%'], alpha: undefined }}
17-
${'hsl(0rad, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0rad', '30%', '60%'], alpha: undefined }}
18-
${'hsl(0grad, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0grad', '30%', '60%'], alpha: undefined }}
19-
${'hsl(0turn, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0turn', '30%', '60%'], alpha: undefined }}
20-
${'hsla(0, 30%, 60%, 0.5)'} | ${{ mode: 'hsl', color: ['0', '30%', '60%'], alpha: '0.5' }}
21-
${'hsla(0deg, 30%, 60%, 0.5)'} | ${{ mode: 'hsl', color: ['0deg', '30%', '60%'], alpha: '0.5' }}
22-
${'hsla(0rad, 30%, 60%, 0.5)'} | ${{ mode: 'hsl', color: ['0rad', '30%', '60%'], alpha: '0.5' }}
23-
${'hsla(0grad, 30%, 60%, 0.5)'} | ${{ mode: 'hsl', color: ['0grad', '30%', '60%'], alpha: '0.5' }}
24-
${'hsla(0turn, 30%, 60%, 0.5)'} | ${{ mode: 'hsl', color: ['0turn', '30%', '60%'], alpha: '0.5' }}
25-
${'hsl(0 30% 60%)'} | ${{ mode: 'hsl', color: ['0', '30%', '60%'], alpha: undefined }}
26-
${'hsl(0deg 30% 60%)'} | ${{ mode: 'hsl', color: ['0deg', '30%', '60%'], alpha: undefined }}
27-
${'hsl(0rad 30% 60%)'} | ${{ mode: 'hsl', color: ['0rad', '30%', '60%'], alpha: undefined }}
28-
${'hsl(0grad 30% 60%)'} | ${{ mode: 'hsl', color: ['0grad', '30%', '60%'], alpha: undefined }}
29-
${'hsl(0turn 30% 60%)'} | ${{ mode: 'hsl', color: ['0turn', '30%', '60%'], alpha: undefined }}
30-
${'hsl(0 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0', '30%', '60%'], alpha: '0.5' }}
31-
${'hsl(0deg 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0deg', '30%', '60%'], alpha: '0.5' }}
32-
${'hsl(0rad 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0rad', '30%', '60%'], alpha: '0.5' }}
33-
${'hsl(0grad 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0grad', '30%', '60%'], alpha: '0.5' }}
34-
${'hsl(0turn 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0turn', '30%', '60%'], alpha: '0.5' }}
35-
${'transparent'} | ${{ mode: 'rgb', color: ['0', '0', '0'], alpha: '0' }}
5+
color | output
6+
${'black'} | ${{ mode: 'rgb', color: ['0', '0', '0'], alpha: undefined }}
7+
${'#0088cc'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: undefined }}
8+
${'#08c'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: undefined }}
9+
${'#0088cc99'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: '0.6' }}
10+
${'#08c9'} | ${{ mode: 'rgb', color: ['0', '136', '204'], alpha: '0.6' }}
11+
${'rgb(0, 30, 60)'} | ${{ mode: 'rgb', color: ['0', '30', '60'], alpha: undefined }}
12+
${'rgba(0, 30, 60, 0.5)'} | ${{ mode: 'rgb', color: ['0', '30', '60'], alpha: '0.5' }}
13+
${'rgb(0 30 60)'} | ${{ mode: 'rgb', color: ['0', '30', '60'], alpha: undefined }}
14+
${'rgb(0 30 60 / 0.5)'} | ${{ mode: 'rgb', color: ['0', '30', '60'], alpha: '0.5' }}
15+
${'rgb(var(--foo), 30, 60)'} | ${{ mode: 'rgb', color: ['var(--foo)', '30', '60'], alpha: undefined }}
16+
${'rgb(0, var(--foo), 60)'} | ${{ mode: 'rgb', color: ['0', 'var(--foo)', '60'], alpha: undefined }}
17+
${'rgb(0, 30, var(--foo))'} | ${{ mode: 'rgb', color: ['0', '30', 'var(--foo)'], alpha: undefined }}
18+
${'rgb(0, 30, var(--foo), 0.5)'} | ${{ mode: 'rgb', color: ['0', '30', 'var(--foo)'], alpha: '0.5' }}
19+
${'rgb(var(--foo), 30, var(--bar))'} | ${{ mode: 'rgb', color: ['var(--foo)', '30', 'var(--bar)'], alpha: undefined }}
20+
${'rgb(var(--foo), var(--bar), var(--baz))'} | ${{ mode: 'rgb', color: ['var(--foo)', 'var(--bar)', 'var(--baz)'], alpha: undefined }}
21+
${'rgb(var(--foo) 30 60)'} | ${{ mode: 'rgb', color: ['var(--foo)', '30', '60'], alpha: undefined }}
22+
${'rgb(0 var(--foo) 60)'} | ${{ mode: 'rgb', color: ['0', 'var(--foo)', '60'], alpha: undefined }}
23+
${'rgb(0 30 var(--foo))'} | ${{ mode: 'rgb', color: ['0', '30', 'var(--foo)'], alpha: undefined }}
24+
${'rgb(0 30 var(--foo) / 0.5)'} | ${{ mode: 'rgb', color: ['0', '30', 'var(--foo)'], alpha: '0.5' }}
25+
${'rgb(var(--foo) 30 var(--bar))'} | ${{ mode: 'rgb', color: ['var(--foo)', '30', 'var(--bar)'], alpha: undefined }}
26+
${'rgb(var(--foo) var(--bar) var(--baz))'} | ${{ mode: 'rgb', color: ['var(--foo)', 'var(--bar)', 'var(--baz)'], alpha: undefined }}
27+
${'hsl(0, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0', '30%', '60%'], alpha: undefined }}
28+
${'hsl(0deg, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0deg', '30%', '60%'], alpha: undefined }}
29+
${'hsl(0rad, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0rad', '30%', '60%'], alpha: undefined }}
30+
${'hsl(0grad, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0grad', '30%', '60%'], alpha: undefined }}
31+
${'hsl(0turn, 30%, 60%)'} | ${{ mode: 'hsl', color: ['0turn', '30%', '60%'], alpha: undefined }}
32+
${'hsla(0, 30%, 60%, 0.5)'} | ${{ mode: 'hsl', color: ['0', '30%', '60%'], alpha: '0.5' }}
33+
${'hsla(0deg, 30%, 60%, 0.5)'} | ${{ mode: 'hsl', color: ['0deg', '30%', '60%'], alpha: '0.5' }}
34+
${'hsla(0rad, 30%, 60%, 0.5)'} | ${{ mode: 'hsl', color: ['0rad', '30%', '60%'], alpha: '0.5' }}
35+
${'hsla(0grad, 30%, 60%, 0.5)'} | ${{ mode: 'hsl', color: ['0grad', '30%', '60%'], alpha: '0.5' }}
36+
${'hsla(0turn, 30%, 60%, 0.5)'} | ${{ mode: 'hsl', color: ['0turn', '30%', '60%'], alpha: '0.5' }}
37+
${'hsl(0 30% 60%)'} | ${{ mode: 'hsl', color: ['0', '30%', '60%'], alpha: undefined }}
38+
${'hsl(0deg 30% 60%)'} | ${{ mode: 'hsl', color: ['0deg', '30%', '60%'], alpha: undefined }}
39+
${'hsl(0rad 30% 60%)'} | ${{ mode: 'hsl', color: ['0rad', '30%', '60%'], alpha: undefined }}
40+
${'hsl(0grad 30% 60%)'} | ${{ mode: 'hsl', color: ['0grad', '30%', '60%'], alpha: undefined }}
41+
${'hsl(0turn 30% 60%)'} | ${{ mode: 'hsl', color: ['0turn', '30%', '60%'], alpha: undefined }}
42+
${'hsl(0 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0', '30%', '60%'], alpha: '0.5' }}
43+
${'hsl(0deg 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0deg', '30%', '60%'], alpha: '0.5' }}
44+
${'hsl(0rad 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0rad', '30%', '60%'], alpha: '0.5' }}
45+
${'hsl(0grad 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0grad', '30%', '60%'], alpha: '0.5' }}
46+
${'hsl(0turn 30% 60% / 0.5)'} | ${{ mode: 'hsl', color: ['0turn', '30%', '60%'], alpha: '0.5' }}
47+
${'hsl(var(--foo), 30%, 60%)'} | ${{ mode: 'hsl', color: ['var(--foo)', '30%', '60%'], alpha: undefined }}
48+
${'hsl(0, var(--foo), 60%)'} | ${{ mode: 'hsl', color: ['0', 'var(--foo)', '60%'], alpha: undefined }}
49+
${'hsl(0, 30%, var(--foo))'} | ${{ mode: 'hsl', color: ['0', '30%', 'var(--foo)'], alpha: undefined }}
50+
${'hsl(0, 30%, var(--foo), 0.5)'} | ${{ mode: 'hsl', color: ['0', '30%', 'var(--foo)'], alpha: '0.5' }}
51+
${'hsl(var(--foo), 30%, var(--bar))'} | ${{ mode: 'hsl', color: ['var(--foo)', '30%', 'var(--bar)'], alpha: undefined }}
52+
${'hsl(var(--foo), var(--bar), var(--baz))'} | ${{ mode: 'hsl', color: ['var(--foo)', 'var(--bar)', 'var(--baz)'], alpha: undefined }}
53+
${'hsl(var(--foo) 30% 60%)'} | ${{ mode: 'hsl', color: ['var(--foo)', '30%', '60%'], alpha: undefined }}
54+
${'hsl(0 var(--foo) 60%)'} | ${{ mode: 'hsl', color: ['0', 'var(--foo)', '60%'], alpha: undefined }}
55+
${'hsl(0 30% var(--foo))'} | ${{ mode: 'hsl', color: ['0', '30%', 'var(--foo)'], alpha: undefined }}
56+
${'hsl(0 30% var(--foo) / 0.5)'} | ${{ mode: 'hsl', color: ['0', '30%', 'var(--foo)'], alpha: '0.5' }}
57+
${'hsl(var(--foo) 30% var(--bar))'} | ${{ mode: 'hsl', color: ['var(--foo)', '30%', 'var(--bar)'], alpha: undefined }}
58+
${'hsl(var(--foo) var(--bar) var(--baz))'} | ${{ mode: 'hsl', color: ['var(--foo)', 'var(--bar)', 'var(--baz)'], alpha: undefined }}
59+
${'transparent'} | ${{ mode: 'rgb', color: ['0', '0', '0'], alpha: '0' }}
3660
`('should parse "$color" to the correct value', ({ color, output }) => {
3761
expect(parseColor(color)).toEqual(output)
3862
})

0 commit comments

Comments
 (0)