Skip to content

Commit adc8dfb

Browse files
Ensure CSS theme() functions are evaluated in media query ranges with collapsed whitespace (#14321)
Fixes #14320 This PR adds `>`, `<`, and `=` as separators into the CSS value parser. This is necessary because [`@media` range context](https://www.w3.org/TR/mediaqueries-4/#mq-range-context) does not require spaces around these operators so something like this is a valid value for the range syntax: ```css @media (40rem<width<=48rem) { /* ... */ } ``` If you add our CSS `theme()` function to the mix, this rule look like that: ```css @media (theme(--breakpoint-sm)<width<=theme(--breakpoint-md)) { /* ... */ } ``` --------- Co-authored-by: Adam Wathan <[email protected]>
1 parent a1d56d8 commit adc8dfb

File tree

4 files changed

+65
-5
lines changed

4 files changed

+65
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Fixed
1111

1212
- Ensure content globs defined in `@config` files are relative to that file ([#14314](https://github.com/tailwindlabs/tailwindcss/pull/14314))
13+
- Ensure CSS `theme()` functions are evaluated in media query ranges with collapsed whitespace ((#14321)[https://github.com/tailwindlabs/tailwindcss/pull/14321])
1314

1415
## [4.0.0-alpha.21] - 2024-09-02
1516

packages/tailwindcss/src/functions.test.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -540,14 +540,15 @@ describe('theme function', () => {
540540
})
541541

542542
describe('in @media queries', () => {
543-
test('@media (min-width: theme(breakpoint.md)) and (max-width: theme(--breakpoint-lg))', async () => {
543+
test('@media (min-width:theme(breakpoint.md)) and (max-width: theme(--breakpoint-lg))', async () => {
544544
expect(
545545
await compileCss(css`
546546
@theme {
547547
--breakpoint-md: 48rem;
548548
--breakpoint-lg: 64rem;
549549
}
550-
@media (min-width: theme(breakpoint.md)) and (max-width: theme(--breakpoint-lg)) {
550+
/* prettier-ignore */
551+
@media (min-width:theme(breakpoint.md)) and (max-width: theme(--breakpoint-lg)) {
551552
.red {
552553
color: red;
553554
}
@@ -566,5 +567,32 @@ describe('theme function', () => {
566567
}"
567568
`)
568569
})
570+
571+
test('@media (width >= theme(breakpoint.md)) and (width<theme(--breakpoint-lg))', async () => {
572+
expect(
573+
await compileCss(css`
574+
@theme {
575+
--breakpoint-md: 48rem;
576+
--breakpoint-lg: 64rem;
577+
}
578+
@media (width >= theme(breakpoint.md)) and (width<theme(--breakpoint-lg)) {
579+
.red {
580+
color: red;
581+
}
582+
}
583+
`),
584+
).toMatchInlineSnapshot(`
585+
":root {
586+
--breakpoint-md: 48rem;
587+
--breakpoint-lg: 64rem;
588+
}
589+
590+
@media (width >= 48rem) and (width < 64rem) {
591+
.red {
592+
color: red;
593+
}
594+
}"
595+
`)
596+
})
569597
})
570598
})

packages/tailwindcss/src/value-parser.test.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,11 @@ describe('parse', () => {
9393
})
9494

9595
it('should handle media query params with functions', () => {
96-
expect(parse('(min-width: 600px) and (max-width:theme(colors.red.500))')).toEqual([
96+
expect(
97+
parse(
98+
'(min-width: 600px) and (max-width:theme(colors.red.500)) and (theme(--breakpoint-sm)<width<=theme(--breakpoint-md))',
99+
),
100+
).toEqual([
97101
{
98102
kind: 'function',
99103
value: '',
@@ -115,6 +119,20 @@ describe('parse', () => {
115119
{ kind: 'function', value: 'theme', nodes: [{ kind: 'word', value: 'colors.red.500' }] },
116120
],
117121
},
122+
{ kind: 'separator', value: ' ' },
123+
{ kind: 'word', value: 'and' },
124+
{ kind: 'separator', value: ' ' },
125+
{
126+
kind: 'function',
127+
value: '',
128+
nodes: [
129+
{ kind: 'function', value: 'theme', nodes: [{ kind: 'word', value: '--breakpoint-sm' }] },
130+
{ kind: 'separator', value: '<' },
131+
{ kind: 'word', value: 'width' },
132+
{ kind: 'separator', value: '<=' },
133+
{ kind: 'function', value: 'theme', nodes: [{ kind: 'word', value: '--breakpoint-md' }] },
134+
],
135+
},
118136
])
119137
})
120138
})

packages/tailwindcss/src/value-parser.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ const DOUBLE_QUOTE = 0x22
111111
const OPEN_PAREN = 0x28
112112
const SINGLE_QUOTE = 0x27
113113
const SPACE = 0x20
114+
const LESS_THAN = 0x3c
115+
const GREATER_THAN = 0x3e
116+
const EQUALS = 0x3d
114117

115118
export function parse(input: string) {
116119
input = input.replaceAll('\r\n', '\n')
@@ -139,7 +142,10 @@ export function parse(input: string) {
139142
// ```
140143
case COLON:
141144
case COMMA:
142-
case SPACE: {
145+
case SPACE:
146+
case LESS_THAN:
147+
case GREATER_THAN:
148+
case EQUALS: {
143149
// 1. Handle everything before the separator as a word
144150
// Handle everything before the closing paren a word
145151
if (buffer.length > 0) {
@@ -157,7 +163,14 @@ export function parse(input: string) {
157163
let end = i + 1
158164
for (; end < input.length; end++) {
159165
peekChar = input.charCodeAt(end)
160-
if (peekChar !== COLON && peekChar !== COMMA && peekChar !== SPACE) {
166+
if (
167+
peekChar !== COLON &&
168+
peekChar !== COMMA &&
169+
peekChar !== SPACE &&
170+
peekChar !== LESS_THAN &&
171+
peekChar !== GREATER_THAN &&
172+
peekChar !== EQUALS
173+
) {
161174
break
162175
}
163176
}

0 commit comments

Comments
 (0)