The CSS @custom-media at-rule allows creating aliases for media queries. This is particularly valuable if you have long or complex media queries that you use multiple times across your codebase. The feature is similar in nature to a media query version of CSS custom properties (CSS variables).
Syntax
The syntax for defining an alias is:
@media (<dashed-ident>) [<media-query-list> | true | false ];For example:
@custom-media --modern-touch (pointer: coarse) and (min-width: 1024px);…where the dashed ident is --modern-touch.
The syntax for using an alias is the same as using any media query, but instead of providing media types or media features, you provide the <dashed-ident> of your defined @custom-media:
@media <dashed-ident> {
/* ... */
}Arguments and Descriptors
<dashed-ident>: A user-defined identifier that must start with two dashes (--), similar to functions or custom properties. Just like custom properties, the name is case-sensitive. For example,--mobile-breakpointand--Mobile-Breakpointwould refer to different custom media definitions.<media-query-list>: A list of media queries, separated by operators.true/false: Always-match / never-match toggles.
Let’s look at how these work in different contexts, such as how they’re scoped, using them with booleans, defining complex logic, setting rules with the CSS range syntax, and even nesting aliases.
Scope and Placement
Unlike custom properties, which are scoped to the element they are defined on (and their children), @custom-media rules are global. They are evaluated in the global scope of the stylesheet and will always apply to the entire document. If multiple @custom-media rules are defined with the same name, the one in scope at the time of evaluation is the one that is used.
When a @media rule uses a custom alias, i.e. the dashed ident, it looks at the current definition of that alias at that point in the stylesheet. If the alias is redefined later, it does not “update” the media queries that were already processed. For example, in this case margin-block: 1rem will only be applied to body if it is fullscreen and not browser despite the later declaration using the same name.
@custom-media --screen-display (display-mode: fullscreen);
@media (--screen-display) {
body {
margin-block: 1rem;
}
}
@custom-media --screen-display (display-mode: browser);Note: This scoping behavior is still being discussed and is subject to change in the future.
Boolean Constants
In the Syntax section above, note that a @custom-media rule can be explicitly set to true or false. This is useful for “toggling” entire blocks of CSS during development or for feature flagging.
Operators and Complex Logic
As @custom-media utilizes the exact same logical operators (and, ,, or, not, only) and grouping rules as @media, you can build complex, parentheses-grouped logic just as you normally would. For a full breakdown of how to use operators, negate features, or hide stylesheets from older browsers, reference the Logic and Operators section of the @media almanac. It is also worth referencing the section on nesting and complex decision-making when building complex queries.
To, for example, construct a query using the and logical operator, you can write this:
@custom-media --modern-touch (pointer: coarse) and (min-width: 1024px);Range Syntax
The same as any other <media-query-list>, @custom-media has support for the ranged media query syntax which uses operators, e.g. greater than (>), less than (<), and equals (=), to evaluate conditions:
/* Old way */
@custom-media --tablet (min-width: 768px) and (max-width: 1024px);
/* New, cleaner way */
@custom-media --tablet (768px <= width <= 1024px);Nested Aliases
One unique feature of @custom-media aliases is that they can reference each other. This allows you to build layered, semantic conditions:
@custom-media --narrow-window (width < 30rem);
@custom-media --small-and-hover (--narrow-window) and (hover: hover);
@media (--small-and-hover) {
/* Styles for mobile-sized screens with hover capabilities */
}However, if a loop is detected, all involved custom media queries are treated as undefined. For instance, if --query-a references --query-b, then --query-b cannot reference --query-a. Similarly, a custom media query cannot refer to itself.
Also be aware of over-nesting, as that can make debugging and identifying which layer of query is having the relevant impact in your browser’s developer tools very difficult.
Example: Defining Common Breakpoints
Instead of remembering if your “tablet” breakpoint is 768px or 800px, you can define it once at the top of your stylesheet.
@custom-media --tablet (min-width: 768px);
.sidebar {
display: none;
@media (--tablet) {
display: block;
}
}Example: Defining Shorthands for Existing Properties
Standard boilerplate such as (prefers-reduced-motion: reduce) can be used many times across a codebase, and those bytes add up. You can use @custom-media to define simpler alternatives:
@custom-media --prefers-reduced-motion (prefers-reduced-motion: reduce);
@media (--prefers-reduced-motion) {
/* ... */
}
@custom-media --js-enabled (scripting: enabled);
@custom-media --js-disabled (scripting: none);
@media (--js-disabled) {
.no-js-banner {
display: block;
}
}There are a great number of Open Props @custom-media Recipes you may consider using.
JavaScript Support
@custom-media aliases are not exposed to the JavaScript matchMedia() method, meaning this code will not work, even if you have the alias defined somewhere on your page.
matchMedia("(--tablet)")Specification
The @custom-media at-rule is defined in the Media Queries Level 5 specification.
Browser Support
Unsupported browsers largely ignore @custom-media, so fallback declarations and progressive enhancement strategies can be advantageous. You can use @supports to check if @custom-media is supported in the user’s browser, like so:
@supports (at-rule(@custom-media)) {
/* ... */
}Ironically, however, at time of writing, the @supports at-rule evaluation functionality doesn’t have full support across browsers (Chrome 148+ only), so you will need to check if it is supported in your case. You can see the discussion on this in CSS Drafts Issue #2463.
Another approach is to use a tool such as PostCSS Custom Media, which will expand the rules in a build step to achieve wider browser support.
@custom-media originally handwritten and published with love on CSS-Tricks. You should really get the newsletter as well.