Skip to content

Commit 8467fc2

Browse files
authored
Validate duplicate @media in media queries (#723)
1 parent 6a5b209 commit 8467fc2

File tree

4 files changed

+45
-23
lines changed

4 files changed

+45
-23
lines changed

.changeset/forty-swans-fail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@vanilla-extract/css': patch
3+
---
4+
5+
Validate duplicate '@media' in media queries

packages/css/src/transformCss.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,9 +329,11 @@ class Stylesheet {
329329
);
330330

331331
forEach(rules, (mediaRule, query) => {
332-
validateMediaQuery(query);
332+
const mediaQuery = `@media ${query}`;
333333

334-
const conditions = [...parentConditions, `@media ${query}`];
334+
validateMediaQuery(mediaQuery);
335+
336+
const conditions = [...parentConditions, mediaQuery];
335337

336338
this.addConditionalRule(
337339
{

packages/css/src/validateMediaQuery.test.ts

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import { validateMediaQuery } from './validateMediaQuery';
33
describe('validateMediaQuery', () => {
44
describe('valid selectors', () => {
55
const validMediaQueries = [
6-
'screen',
7-
'screen, print',
8-
'screen and (max-width: 600px)',
9-
'(min-width: 5rem)',
10-
'(min-width: 30em) and (orientation: landscape)',
11-
'only screen and (min-width: 320px) and (max-width: 480px) and (resolution: 150dpi)',
12-
'not screen and (color), print and (color)',
13-
'(prefers-reduced-motion)',
14-
'(prefers-reduced-motion: no-preference)',
6+
'@media screen',
7+
'@media screen, print',
8+
'@media screen and (max-width: 600px)',
9+
'@media (min-width: 5rem)',
10+
'@media (min-width: 30em) and (orientation: landscape)',
11+
'@media only screen and (min-width: 320px) and (max-width: 480px) and (resolution: 150dpi)',
12+
'@media not screen and (color), print and (color)',
13+
'@media (prefers-reduced-motion)',
14+
'@media (prefers-reduced-motion: no-preference)',
1515
];
1616

1717
validMediaQueries.forEach((query) =>
@@ -23,8 +23,9 @@ describe('validateMediaQuery', () => {
2323

2424
describe('invalid media queries', () => {
2525
it('empty query', () => {
26-
expect(() => validateMediaQuery('')).toThrowErrorMatchingInlineSnapshot(`
27-
"Invalid media query: \\"\\"
26+
expect(() => validateMediaQuery('@media '))
27+
.toThrowErrorMatchingInlineSnapshot(`
28+
"Invalid media query: \\"@media \\"
2829
2930
Query is empty
3031
@@ -33,9 +34,9 @@ describe('validateMediaQuery', () => {
3334
});
3435

3536
it('random query', () => {
36-
expect(() => validateMediaQuery('random query'))
37+
expect(() => validateMediaQuery('@media random query'))
3738
.toThrowErrorMatchingInlineSnapshot(`
38-
"Invalid media query: \\"random query\\"
39+
"Invalid media query: \\"@media random query\\"
3940
4041
Unknown ident 'random' in media query
4142
@@ -44,9 +45,9 @@ describe('validateMediaQuery', () => {
4445
});
4546

4647
it('(min-height: 600px', () => {
47-
expect(() => validateMediaQuery('(min-height: 600px'))
48+
expect(() => validateMediaQuery('@media (min-height: 600px'))
4849
.toThrowErrorMatchingInlineSnapshot(`
49-
"Invalid media query: \\"(min-height: 600px\\"
50+
"Invalid media query: \\"@media (min-height: 600px\\"
5051
5152
Invalid media condition
5253
Expected media condition after '('
@@ -56,9 +57,9 @@ describe('validateMediaQuery', () => {
5657
});
5758

5859
it('min-width: 600px)', () => {
59-
expect(() => validateMediaQuery('min-width: 600px)'))
60+
expect(() => validateMediaQuery('@media min-width: 600px)'))
6061
.toThrowErrorMatchingInlineSnapshot(`
61-
"Invalid media query: \\"min-width: 600px)\\"
62+
"Invalid media query: \\"@media min-width: 600px)\\"
6263
6364
Unknown ident 'min-width' in media query
6465
@@ -67,14 +68,27 @@ describe('validateMediaQuery', () => {
6768
});
6869

6970
it('prefers-reduced-motion: no-preference', () => {
70-
expect(() => validateMediaQuery('prefers-reduced-motion: no-preference'))
71-
.toThrowErrorMatchingInlineSnapshot(`
72-
"Invalid media query: \\"prefers-reduced-motion: no-preference\\"
71+
expect(() =>
72+
validateMediaQuery('@media prefers-reduced-motion: no-preference'),
73+
).toThrowErrorMatchingInlineSnapshot(`
74+
"Invalid media query: \\"@media prefers-reduced-motion: no-preference\\"
7375
7476
Unknown ident 'prefers-reduced-motion' in media query
7577
7678
Read more on MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries"
7779
`);
7880
});
81+
82+
it('Double media: @media @media screen and (min-width: 640px)', () => {
83+
expect(() =>
84+
validateMediaQuery('@media @media screen and (min-width: 640px)'),
85+
).toThrowErrorMatchingInlineSnapshot(`
86+
"Invalid media query: \\"@media @media screen and (min-width: 640px)\\"
87+
88+
Expected media condition or media prefix
89+
90+
Read more on MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries"
91+
`);
92+
});
7993
});
8094
});

packages/css/src/validateMediaQuery.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ const createMediaQueryError = (mediaQuery: string, msg: string) =>
1313
);
1414

1515
export const validateMediaQuery = (mediaQuery: string) => {
16-
if (mediaQuery === '') {
16+
// Empty queries will start with '@media '
17+
if (mediaQuery === '@media ') {
1718
throw createMediaQueryError(mediaQuery, 'Query is empty');
1819
}
1920

0 commit comments

Comments
 (0)