Skip to content

Commit 32f309f

Browse files
authored
feat: validate media queries (#651)
1 parent 9d38585 commit 32f309f

File tree

6 files changed

+83
-0
lines changed

6 files changed

+83
-0
lines changed

.changeset/moody-poems-tan.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@vanilla-extract/css': minor
3+
---
4+
5+
Add CSS media query validation

packages/css/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
"@emotion/hash": "^0.8.0",
111111
"@vanilla-extract/private": "^1.0.3",
112112
"chalk": "^4.1.1",
113+
"css-mediaquery": "^0.1.2",
113114
"css-what": "^5.0.1",
114115
"cssesc": "^3.0.0",
115116
"csstype": "^3.0.7",
@@ -119,6 +120,7 @@
119120
"outdent": "^0.8.0"
120121
},
121122
"devDependencies": {
123+
"@types/css-mediaquery": "^0.1.1",
122124
"@types/cssesc": "^3.0.0"
123125
}
124126
}

packages/css/src/transformCss.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { forEach, omit, mapKeys } from './utils';
2020
import { validateSelector } from './validateSelector';
2121
import { ConditionalRuleset } from './conditionalRulesets';
2222
import { simplePseudos, simplePseudoLookup } from './simplePseudos';
23+
import { validateMediaQuery } from './validateMediaQuery';
2324

2425
const UNITLESS: Record<string, boolean> = {
2526
animationIterationCount: true,
@@ -328,6 +329,8 @@ class Stylesheet {
328329
);
329330

330331
forEach(rules, (mediaRule, query) => {
332+
validateMediaQuery(query);
333+
331334
const conditions = [...parentConditions, `@media ${query}`];
332335

333336
this.addConditionalRule(
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { validateMediaQuery } from './validateMediaQuery';
2+
3+
describe('validateMediaQuery', () => {
4+
describe('valid selectors', () => {
5+
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+
];
14+
15+
validMediaQueries.forEach((query) =>
16+
it(query, () => {
17+
expect(() => validateMediaQuery(query)).not.toThrow();
18+
}),
19+
);
20+
});
21+
22+
describe('invalid media queries', () => {
23+
const invalidMediaQueries = [
24+
'',
25+
'random query',
26+
'(min-height: 600px',
27+
'min-width: 600px)',
28+
];
29+
30+
invalidMediaQueries.forEach((query) =>
31+
it(query, () => {
32+
expect(() => validateMediaQuery(query)).toThrow();
33+
}),
34+
);
35+
});
36+
});
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import outdent from 'outdent';
2+
import { parse } from 'css-mediaquery';
3+
4+
const mediaTypes = ['all', 'print', 'screen'];
5+
6+
export const validateMediaQuery = (mediaQuery: string) => {
7+
const { type, expressions } = parse(mediaQuery)?.[0];
8+
9+
const isAllQuery = mediaQuery === 'all';
10+
const isValidType = mediaTypes.includes(type);
11+
12+
// If the parser returns all for the type, we should have expressions
13+
// or the query should match 'all' otherwise it is invalid
14+
if (!isValidType || (!isAllQuery && type === 'all' && !expressions.length)) {
15+
throw new Error(
16+
outdent`
17+
Invalid media query: ${mediaQuery}
18+
19+
A media query can contain an optional media type and any number of media feature expressions.
20+
21+
Read more on MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries
22+
`,
23+
);
24+
}
25+
};

pnpm-lock.yaml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)