Named media queries with @custom-media
Breakpoints — the widths where the layout changes — repeat across the whole project. A named media query, @custom-media, lets you write a condition once and refer to it by name afterwards. It is a draft CSS standard that browsers do not understand yet, so postcss-preset-env replaces the names with the conditions themselves.
Declaring and using
The plugin is already enabled in postcss.config.js. Declare the name at the top of styles/main.css and use it in @media inside parentheses:
/* styles/main.css */
@custom-media --tablet (width >= 768px);
.post-list {
display: grid;
gap: 16px;
}
@media (--tablet) {
.post-list {
grid-template-columns: 1fr 1fr;
}
}Like a CSS variable, the name starts with two dashes. After npx postcss styles/main.css -o css/main.css:
.post-list {
display: grid;
grid-gap: 16px;
gap: 16px;
}
@media (min-width: 768px) {
.post-list {
grid-template-columns: 1fr 1fr;
}
}The declaration is gone, and (--tablet) was replaced with the condition. The plugin also turned the range syntax width >= 768px into the familiar min-width: 768px and added grid-gap — the old name of the gap property for grids.
<style>
.post-list {
display: grid;
grid-gap: 16px;
gap: 16px;
}
@media (min-width: 768px) {
.post-list {
grid-template-columns: 1fr 1fr;
}
}
.post-list article {
padding: 16px;
background: #f1f5f9;
}
</style>
<div class="post-list">
<article>Getting to know PostCSS</article>
<article>Setting up Autoprefixer</article>
<article>Minifying CSS</article>
</div>In a narrow window the posts form one column, and from 768 pixels on — two.
Several names
Declare all breakpoints together and combine names with and:
/* styles/main.css */
@custom-media --dark (prefers-color-scheme: dark);
@custom-media --wide (width >= 1024px);
@media (--dark) and (--wide) {
.post-list {
gap: 32px;
}
}@media (prefers-color-scheme: dark) and (min-width: 1024px) {
.post-list {
gap: 32px;
}
}Now moving a breakpoint means changing one declaration line.
Why not a variable
CSS variables like var(--tablet) do not work inside @media: media queries are evaluated before variable values. So conditions need a separate mechanism, and @custom-media is its future standard syntax.
Common mistake
Declaring the names in one file and using them in another that is processed separately. The plugin sees only the current file: without a declaration it silently leaves @media (--tablet) as is, and the browser ignores the whole block. Include the file with declarations through @import and postcss-import (next chapter) — then all files are joined before processing.