CSS @custom-media: Create Reusable Media Query Aliases for Cleaner Stylesheets
If you’ve ever found yourself copying and pasting the same media query across dozens of CSS files, you already know the frustration. Change the breakpoint once and you have to hunt down every instance. Miss one and your layout breaks in unexpected ways.
The CSS @custom-media at-rule is designed to fix exactly that problem. It lets you define a named alias for any media query — then reuse that alias anywhere in your stylesheet. Think of it as CSS custom properties, but for media queries.
In this guide, we’ll break down everything you need to know about @custom-media: how it works, how to write it, real-world use cases, and current browser support. Let’s dive in.
What Is @custom-media?
The @custom-media at-rule is part of the Media Queries Level 5 specification. It allows you to define a reusable alias — an identifiable name — for a media query or a list of media queries.
Instead of writing (min-width: 768px) over and over, you define it once as --tablet and reference that name wherever you need it.
This is especially valuable in large codebases where the same media queries appear repeatedly. Centralizing your breakpoints and feature queries makes maintenance dramatically easier.
Basic Syntax
Defining a custom media alias follows this pattern:
@custom-media <dashed-ident> [<media-query-list> | true | false];
The <dashed-ident> is a user-defined name that must begin with two dashes (--), just like CSS custom properties.
Here’s a simple real-world example:
@custom-media --tablet (min-width: 768px);
Once defined, you use the alias inside a standard @media rule like this:
@media (--tablet) {
.sidebar {
display: block;
}
}
Clean, readable, and easy to update in one place.
Arguments and Descriptors Explained
Let’s break down the three possible values you can assign to a custom media alias:
<dashed-ident>
This is your alias name. It must start with -- and is case-sensitive. That means --mobile-breakpoint and --Mobile-Breakpoint are treated as two completely different aliases.
<media-query-list>
This is one or more standard media queries, separated by logical operators like and, or, or commas.
true / false
You can set an alias to always match (true) or never match (false). This is handy for toggling entire CSS blocks during development or using feature flags.
@custom-media --debug-mode true;
@media (--debug-mode) {
* {
outline: 2px solid red;
}
}
Set it to false and the block is silently ignored — no need to comment out code.
Scope and Placement
One important distinction between @custom-media and CSS custom properties is scope.
Custom properties are scoped to the element they’re defined on. @custom-media rules, by contrast, are global. They apply to the entire document regardless of where you define them in the stylesheet.
However, if you define the same alias twice, the version that was in scope when the @media rule was processed is the one that gets used. Redefining an alias later does not retroactively update earlier media rules.
@custom-media --screen-display (display-mode: fullscreen);
@media (--screen-display) {
body {
margin-block: 1rem;
}
}
@custom-media --screen-display (display-mode: browser);
In the example above, the margin-block: 1rem rule applies only when display-mode is fullscreen, not browser, even though the alias is redefined later.
Note: This scoping behavior is still being actively discussed by the CSS working group and may change in future spec revisions.
Using Logical Operators for Complex Queries
@custom-media supports the same logical operators as regular @media rules. You can use and, or, not, only, and commas to build complex conditional logic.
@custom-media --modern-touch (pointer: coarse) and (min-width: 1024px);
This alias matches only when a coarse pointer (like a finger on a touchscreen) is present and the viewport is at least 1024px wide.
You can group conditions with parentheses just as you normally would in a @media rule.
Range Syntax Support
The newer CSS media query range syntax is also fully supported inside @custom-media. This means you can write cleaner, more readable range conditions using comparison operators:
/* Old way */
@custom-media --tablet (min-width: 768px) and (max-width: 1024px);
/* New, cleaner way */
@custom-media --tablet (768px <= width <= 1024px);
The range syntax is especially useful for defining breakpoint ranges that include both a minimum and maximum width, which is a common pattern in responsive design.
Nesting Aliases Inside Other Aliases
One of the most powerful — and unique — features of @custom-media is the ability to reference one alias from within another. This lets you 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 small screens with hover capability */
}
This is excellent for building a structured, meaningful hierarchy of responsive conditions.
There are a few important caveats to keep in mind:
- No circular references: If
--query-areferences--query-b, then--query-bcannot reference--query-a. Any loop is treated as undefined and the queries are ignored. - No self-references: An alias cannot reference itself.
- Avoid over-nesting: Deep nesting can make it very difficult to debug which query layer is affecting your layout in browser developer tools.
Practical Use Cases
Defining Common Breakpoints
Stop trying to remember whether your tablet breakpoint is 768px or 800px. Define it once at the top of your stylesheet and reference it everywhere.
@custom-media --mobile (max-width: 767px);
@custom-media --tablet (768px <= width <= 1024px);
@custom-media --desktop (min-width: 1025px);
.hero {
font-size: 1.25rem;
@media (--tablet) {
font-size: 1.5rem;
}
@media (--desktop) {
font-size: 2rem;
}
}
Shorthand for Accessibility Queries
Standard accessibility media queries like (prefers-reduced-motion: reduce) can appear dozens of times in a large codebase. Aliases make them shorter and more readable.
@custom-media --prefers-reduced-motion (prefers-reduced-motion: reduce);
@custom-media --prefers-dark-mode (prefers-color-scheme: dark);
@custom-media --prefers-high-contrast (prefers-contrast: high);
@media (--prefers-reduced-motion) {
.animated-element {
animation: none;
}
}
@media (--prefers-dark-mode) {
body {
background: #121212;
color: #f0f0f0;
}
}
JavaScript Scripting Feature Detection
You can also use @custom-media to detect whether JavaScript is enabled or disabled in the browser.
@custom-media --js-enabled (scripting: enabled);
@custom-media --js-disabled (scripting: none);
@media (--js-disabled) {
.no-js-banner {
display: block;
}
}
This is a great progressive enhancement technique for ensuring your UI remains functional without JavaScript.
What About JavaScript’s matchMedia()?
Here’s an important limitation to be aware of: @custom-media aliases are not accessible from JavaScript’s matchMedia() method.
The following code will not work, even if the alias is defined in your stylesheet:
matchMedia("(--tablet)") // Does not work
If you need to evaluate media queries in JavaScript, you’ll need to use the full query string directly. Keep this in mind if you’re building functionality that depends on JavaScript-driven responsive behavior.
Browser Support and Fallback Strategies
As of the time of writing, @custom-media is part of the Media Queries Level 5 spec and browser support is still evolving. Unsupported browsers will simply ignore the rules, so your layout won’t break — it just won’t benefit from the aliases.
You can use @supports to detect support conditionally:
@supports (at-rule(@custom-media)) {
/* Styles that rely on @custom-media */
}
However, the @supports at-rule() syntax itself currently only works in Chrome 148+, so you’ll need to verify compatibility for your specific audience.
For broader browser support today, the most reliable approach is to use a build-time tool like