Using PostCSS Preset Env Stage‑3 to Write Nesting and Custom Media Queries Today
Learn how PostCSS Preset Env stage‑3 lets you use nesting and custom media queries today while outputting browser‑compatible CSS.
22 Sept 2025, 02:29 UTC

The problem: modern CSS syntax isn’t yet understood by browsers
When you start using CSS features that are still in the specification process—such as nesting rules or custom media queries—browsers either ignore them or apply them incorrectly. This forces you to either wait for full support or write verbose fallbacks by hand.
Why Preset Env stage‑3 helps
PostCSS Preset Env reads your browserslist and automatically translates stage‑3 CSS into equivalent syntax that the listed browsers already understand. Stage‑3 means the feature has reached Candidate Recommendation, so it is stable enough for experimental use while still being subject to minor changes.
Setting up the pipeline
Add the plugin as a dev dependency and create a postcss.config.js file that loads postcss-preset-env with the desired stage and an optional source‑map flag.
// postcss.config.js
module.exports = {
plugins: [
require('postcss-preset-env')({
stage: 3,
browserslist: 'defaults',
map: true
})
]
};
Install the peer dependencies that the preset may need, for example postcss-nested if you plan to use nesting.
Worked example: nesting and @custom‑media
Consider the following source CSS (src/styles.css):
/* src/styles.css */
:root {
--color-primary: #0066ff;
}
@custom-media --viewport-medium (width >= 500px);
.card {
background: var(--color-primary);
padding: 1rem;
@media (--viewport-medium) {
padding: 2rem;
}
.title {
font-size: 1.5rem;
color: #fff;
}
}
When PostCSS runs with the configuration above, the preset expands the custom media query, rewrites the nesting into flat selectors, and maps logical properties if any were used. The resulting CSS (dist/styles.css) will look roughly like this:
/* dist/styles.css */
:root {
--color-primary: #0066ff;
}
.card {
background: var(--color-primary);
padding: 1rem;
}
@media (width >= 500px) {
.card {
padding: 2rem;
}
.card .title {
font-size: 1.5rem;
color: #fff;
}
}
Note: the exact output depends on the browserslist; targeting modern browsers may omit some fallbacks.
Trade‑off: extra polyfills and browserslist tuning
Enabling stage‑3 features can add polyfills that increase file size. If your browserslist includes older browsers, the preset will insert additional vendor‑prefixed properties or JavaScript‑based fallbacks. To keep the output slim, tailor the list to the actual browsers you support, for example:
browserslist: [
'Chrome >= 90',
'Firefox >= 88',
'Safari >= 14'
]
Run a quick size comparison (e.g., wc -c dist/styles.css) with and without the preset to see the impact.
Closing: next steps
Start by adding a single stage‑3 feature you miss, verify the transformed output with browser devtools, and adjust your browserslist until the generated CSS matches your support matrix. Keep the preset under version control so that future updates to the specification are applied automatically when you bump the plugin version.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.