css-mediaquery
css-mediaquery is a dependency-free CommonJS module with two functions: parse() converts a CSS Media Queries Level 3-style string into a small array AST, and match() evaluates that string against a JavaScript object representing a device or browser state. It handles comma-separated alternatives, not and only modifiers, min/max features, orientation, dimensions, resolution, aspect ratios, and several legacy device features. It is often used in server rendering or tests where window.matchMedia is unavailable, but it is not a browser engine and its unit model is fixed in code.
css-mediaquery remains useful as a frozen compatibility layer for simple SSR and tests. Do not treat its answer as browser truth for modern queries, relative units, calculated values, or exact physical-unit conversion.
Use it if
- You maintain server-side rendering or tests already depending on this package's match(query, values) contract
- Your queries stay within classic Media Queries Level 3 syntax and use simple numeric values
- You need a tiny parser AST for inspection rather than a full CSS stylesheet parser
- You can supply an explicit synthetic device state including a media type for every match
- You need modern Media Queries syntax or features: open issues request prefers-reduced-motion, color-gamut, and any-pointer support, and the parser does not implement range syntax such as width >= 600px
- You need browser-accurate units: em and rem are hard-coded to 16px, calc() crashes are reported, and the source's point and pica conversions do not match standard CSS pixel ratios
- You want maintained releases: npm 0.1.2 was published in January 2014, while the 2024 commit only corrected repository license metadata and still has not produced the requested npm release
- You need TypeScript or ESM packaging: the module provides one CommonJS file with no declarations, export map, or browser-native module build
- You want to parse once and match repeatedly: the public match() reparses the string every call, while an old open pull request for matching an existing AST was never merged
Setup reality
npm install css-mediaquery is the entire installation. There are no runtime dependencies, peers, native builds, credentials, or config files. Require the module and call either match(query, values) or parse(query). The work is in constructing values correctly. The README says type is required and cannot be all; use screen, print, or the concrete medium you are simulating. Dimension and resolution values may be numbers or unit-bearing strings, but the conversion model is not tied to a DOM, root font size, zoom, viewport, or device. em and rem always mean 16px. Width-like values recognize px, em, rem, cm, mm, in, pt, and pc, while resolution recognizes dpi, dpcm, and dppx. Aspect ratios can be decimals or strings such as 16/9. Missing or falsy feature values fail a positive expression, which means zero is mishandled and has an open fix pull request. parse() throws SyntaxError for malformed query structure; match() calls parse internally, so malformed user input also throws rather than returning false. Unknown feature names can enter the AST and fall through to strict value equality, which can look like support without browser semantics. The AST is descriptive only: there is no serializer or public match-from-AST function. For SSR, keep the server's assumed viewport policy explicit and expect hydration differences if the actual client differs. This package cannot observe changes, attach listeners, or replace the live window.matchMedia object on its own.
Patterns
Match a minimum viewport widthmatch-min-width
const mediaQuery = require('css-mediaquery');
const matches = mediaQuery.match('screen and (min-width: 40em)', {
type: 'screen',
width: '1024px',
});The package always converts em and rem using 16px, regardless of root font size, browser zoom, or supplied values.
Match any comma-separated querymatch-query-list
const matches = mediaQuery.match(
'screen and (min-width: 900px), print',
{ type: 'print', width: '800px' }
);
// trueA comma is treated as logical OR by parsing separate query objects and returning true when any one matches.
Match screen orientationmatch-orientation
mediaQuery.match('screen and (orientation: landscape)', {
type: 'screen',
orientation: 'landscape',
});The library compares the provided orientation string case-insensitively; it does not derive orientation from width and height.
Compare resolution across unitsmatch-resolution
mediaQuery.match('(min-resolution: 2dppx)', {
type: 'screen',
resolution: '192dpi',
});
// truedppx is converted using 96 dpi and dpcm through the source's conversion formula. Pass units explicitly for readable fixtures.
Match an aspect ratiomatch-aspect-ratio
mediaQuery.match('(min-aspect-ratio: 16/9)', {
type: 'screen',
'aspect-ratio': '1.8',
});
// trueFeature keys with hyphens must be quoted in object literals. The package converts ratio strings to decimal numbers.
Evaluate a negated media typematch-negated-query
mediaQuery.match('not print and (color)', {
type: 'screen',
color: 8,
});
// truenot inverts the whole query result. The only modifier is parsed but otherwise ignored, matching its legacy compatibility purpose.
Inspect a media query as an ASTparse-query-ast
const ast = mediaQuery.parse(
'screen and (min-width: 48em) and (orientation: landscape)'
);
for (const expression of ast[0].expressions) {
console.log(expression.modifier, expression.feature, expression.value);
}The parser preserves values as strings and does not expose a serializer. match() cannot accept this AST, only the original string.
Handle invalid query syntaxcatch-invalid-query
function safeMatch(query, values) {
try {
return mediaQuery.match(query, values);
} catch (error) {
if (error instanceof SyntaxError) return false;
throw error;
}
}match reparses on every call and can throw SyntaxError. Conversion bugs can also throw other errors, so only swallow the syntax case you intend.
Build a minimal matchMedia result for SSRcreate-ssr-matchmedia
function ssrMatchMedia(query) {
return {
matches: mediaQuery.match(query, { type: 'screen', width: '1024px' }),
media: query,
onchange: null,
addListener() {},
removeListener() {},
addEventListener() {},
removeEventListener() {},
dispatchEvent() { return false; },
};
}This is a static server assumption, not a live MediaQueryList. Client hydration can disagree when the actual viewport differs.
Match legacy device features explicitlysupply-device-features
mediaQuery.match(
'screen and (min-device-width: 768px) and (min-color: 8)',
{
type: 'screen',
'device-width': '1024px',
color: 24,
}
);The values object is not inferred from Node or a browser. Every referenced feature must be present and truthy.
Account for falsy zero valuesavoid-zero-value-bug
const values = {
type: 'screen',
monochrome: String(device.monochromeDepth),
};
const matches = mediaQuery.match('(monochrome: 0)', values);A numeric 0 is treated as missing by the source's if (!value) check. A string can bypass that check, but test semantics carefully because an open pull request targets this bug.
Memoize repeated fixed-state matches outside the packagecache-query-result
const cache = new Map();
function matchAtDesktop(query) {
if (!cache.has(query)) {
cache.set(query, mediaQuery.match(query, {
type: 'screen', width: '1280px'
}));
}
return cache.get(query);
}The public match function parses the query each time. Cache only when both query and simulated device values are truly fixed.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| media-query-parser | npm | You want a dedicated parser for media-query text and do not need this package's device-state matcher |
| postcss-media-query-parser | npm | You are already in a PostCSS workflow and need a media-query AST for transforms |
| matchmediaquery | npm | You primarily need server-side query matching and prefer a package focused on that operation |