postcss-preset-env

PostCSS Bundler

PostCSS Bundler is a standards compliant reference CSS bundler.

It is built around @import statements, similarly to how esbuild, postcss-import or lightningcss bundle CSS.

@import url('variables.css');
@import url('typography.css');
@import url('components/not-a-card.css');

Why this matters now

Previously bundling CSS based on @import was relatively simple.

There were always a few features (@namespace, @charset, ...) that weren't or couldn't be implemented correctly in bundlers but these also happened to be obscure and rarely used features.

CSS Bundlers had the luxury of getting by with naive implementations.
Even with duplicate and cyclical imports it was largely invisible if a bundler incorrectly only included the first or the last import.

Today however this is no longer true.

We now have cascade layers, support conditions, and soon even scoping in @import statements.
With the addition of these features, the source order is more important than ever and bundlers need to be updated so that you can use all of the features in CSS.

@import url('all-the-styles.css')
	layer(base)
	supports(selector(&))
	(prefers-color-scheme: dark);

css-import-tests

We created an extensive test suite for the behavior of @import in browsers and how these can be mimicked by bundlers. It is not limited to testing what bundlers can do, it also tests some things that can not be implemented by bundlers.

The goal of this test suite is not to score different bundlers against each other, but to promote interop.
We reached out to the maintainers of esbuild and lightningcss so that they are aware of this effort and so that we can coordinate our efforts.

If there are other CSS bundlers that we haven't thought of, please let us know in a GitHub issue

In particular, we want to thank Evan from esbuild as they immediately jumped on this and also contributed back to the test suite. In a very short time they fixed a large number of small discrepancies in esbuild. They also provided insights and very neat tricks that made sub-features possible that we had already given up on.

By sharing css-import-tests and working together with other tool creators we take away part of the workload of each maintainer.

@csstools/postcss-bundler

a.k.a PostCSS Bundler

This is our reference implementation of a bundler that passes as many tests from css-import-tests as possible.

Our vision and goal for this package is that you should be able to turn it on or off and that your CSS should just work either way.

To see more examples you can visit the general docs or you can experiment with the minimal PostCSS setup recipe.

Url rewriting/rebasing

A feature currently unique to PostCSS Bundler is that it also rebases url values. You just write correct file paths in url() functions and PostCSS Bundler will update relative paths so that they still work from the bundled file.
This gives a better dev experience because your file links will actually work in your code editor.

imports of node modules

Importing npm packages is not a standard feature and we decided not to add magic for it.
Instead, we added an explicit and expressive API to reference packages found in node_modules directories.

@import url("node_modules:open-props/red");

By adding the node_modules: url scheme you make it clear to PostCSS Bundler that it should use the module resolution algorithms to find your CSS. This also supports conditional exports.

We decided against the npm: url scheme because this is already used in Deno.
In Deno, it also implies an automatic download which is not something we can or want to implement.
You still need to use your regular JavaScript tooling to download and manage npm packages even when they contain CSS files.

node_modules: is a bit longer but it is absolutely clear in its meaning and purpose.

Going toolless

Ultimately we hope that PostCSS Bundler is frictionless and that it gives you the option to go toolless.
You might still want to bundle for production to prevent a waterfall during load, but a bundler shouldn't be required during development.

Stylelint plugin

Not every valid @import statement can be correctly processed by CSS bundlers.
Some aspects only make sense when interpreted by a real browser, not a build tool.

To warn you about these cases we created a Stylelint plugin, @csstools/stylelint-no-invalid-at-import-rules-when-bundling.

This Stylelint plugin can be used with any standards compliant CSS bundler as they will all have the same edge cases.

postcss-import

PostCSS Bundler is partly based on postcss-import and we have contributed many fixes to it during this process. We will continue to improve this plugin as it is a widely used bundler for PostCSS.

There were however a few breaking changes we wanted to make and a new plugin makes this simpler and easier.

The largest difference is that we want the default config to be standards compliant so that your config is simpler and smaller.

const postcss = require('postcss');
const postcssBundler = require('@csstools/postcss-bundler');

postcss([
	/* this just works, no other config is needed */
	postcssBundler()
]).process(YOUR_CSS /*, processOptions */);

Comparison with any other tool

Given the current state of "tech Twitter", we feel the need to call out specifically that none of this effort is competitive.
We do not care about download numbers and we are not "disrupting" anything.

Our only intention is to make all tools better for all CSS authors.
That is why we did not only create a new bundler but also created the means for all other bundlers to improve.
We care deeply about diversity in tooling and interop between those tools.