Skip to content

[Docs]: LightningCssMinimizerRspackPlugin default targets ("fully supports es6") can inflate CSS by downleveling modern selectors #14816

Description

@TrevorBurnham

System Info

System:
  OS: macOS 26.6
  CPU: (15) arm64 Apple M5 Pro
Binaries:
  Node: 24.15.0
  npm: 11.12.1
Browsers:
  Chrome: 150.0.7871.125
  Firefox: 151.0.4
  Safari: 26.5
npmPackages:
  @rspack/cli: 2.1.4
  @rspack/core: 2.1.4

Details

When LightningCssMinimizerRspackPlugin is used without minimizerOptions.targets, and the project's target is not a browserslist-related value (e.g. the default target: "web"), the plugin falls back to targets: ["fully supports es6"] — a ~2015-era browser baseline.

With that baseline, Lightning CSS treats modern CSS as something it must downlevel for old browsers, so it expands rules instead of shrinking them. Modern logical / RTL syntax is the worst case:

  • text-align: start / end → duplicated into LTR and RTL rules gated on a 19-entry :lang(...) list
  • :dir(rtl) → rewritten to that same :lang(...) list
  • each such rule is then emitted three times, once for :-webkit-any(), :-moz-any(), and :is()
  • logical box properties (padding-inline-*, border-block-*, …) → expanded to physical pairs

For a stylesheet that leans on these features, minified output can be larger than the unminified input. In the minimal repro, a 1.3 KB stylesheet becomes 12 KB after minification (9.2×). On a real app using a component library with pervasive logical/RTL CSS (CloudScape), this inflated total emitted CSS ~2.7× versus webpack 5 (9.8 MB → 3.6 MB); webpack's cssnano-based minifier reads browserslist and does not downlevel.

Reproduces identically on the css-loader + CssExtractRspackPlugin chain and on native experiments.css, confirming it is the minimizer, not the CSS pipeline.

This is partly a docs issue. The plugin docs point the wrong way on two counts:

  1. They say the ES6 fallback "ensures that minification does not introduce advanced syntax that could cause browser incompatibility." In practice the fallback causes aggressive downleveling that multiplies rule count and inflates size. The size cost of the fallback isn't mentioned and is easy to hit by accident.
  2. They say the plugin "excludes all features by default to avoid syntax downgrading during the minimize process." This does not match observed behavior — with no exclude set, downgrading clearly still happens. Either the default isn't actually "exclude all features", or the sentence needs correcting.

Workaround: pass explicit targets (any browserslist query, or your project's browserslist config):

new rspack.LightningCssMinimizerRspackPlugin({
  minimizerOptions: { targets: ["chrome >= 120", "firefox >= 120", "safari >= 17"] },
});

This restores expected behavior (in the real app, it shrank CSS below webpack's output).

Suggested resolution (docs, and optionally code):

  1. Warn on the plugin page that when targets is unset and target is not browserslist-related, the "fully supports es6" fallback can significantly increase CSS size by downleveling modern syntax; recommend setting minimizerOptions.targets.
  2. Reconcile the "excludes all features by default" sentence with actual behavior (if downgrading happens by default, that's either a docs error or a code bug).
  3. On the target inheritance page, make explicit that target: "web" (and other non-browserslist targets) does not feed targets into the CSS minimizer, so the ES6 fallback applies.
  4. Optionally, consider defaulting the minimizer's targets to the project's resolved browserslist when available (matching css-minimizer-webpack-plugin/cssnano and builtin:lightningcss-loader).

Reproduce link

https://github.com/TrevorBurnham/rspack-css-minrepro

Reproduce Steps

npm install
npm run build       # default targets -> dist/main.css
npm run build:fixed # explicit modern targets -> dist-fixed/main.css
npm run measure

Observed (@rspack/core@2.1.4; also present in 2.1.2):

  variant                              | CSS bytes | :-webkit-any( expansions
  -------------------------------------|-----------|-------------------------
  source styles.css                    |      1314 |      0
  minified (default targets, BUGGY)    |     12093 |     16
  minified (modern targets, FIXED)     |       490 |       0

  BUG: minified output is 9.20x the size of the unminified source.

npm run build:native (native experiments.css, no css-loader) produces byte-identical bloat (12,093 bytes), confirming the issue is in the minimizer.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions