#1211·tsdx

allow CSS nesting

Author: awkaleCreated Mar 14, 2026Updated Mar 14, 2026

Problem Statement

When building a library with tsdx v2, component-level CSS files (those imported directly in .tsx files like import './drawer.css') are processed by bunchee's inlineCss rollup plugin, which runs clean-css v5.3.3 with default options (new CleanCSS({})).

clean-css v5.x has no support for native CSS nesting. Its tokenizer only handles { at Level.RULE for CSS custom property values and @page margin boxes — there is no handler for the native nesting case (e.g. .foo { &:hover { color: red } }). When it encounters nested { in a rule context, it falls through to a generic buffer push, corrupting the token stream and silently mangling or dropping the nested styles in the output.

This means any component-level CSS file that uses native CSS nesting syntax will produce broken output after the tsdx build, with no error or warning.

Proposed Solution

Replace clean-css with a CSS minifier that supports native CSS nesting, such as Lightning CSS or cssnano (via PostCSS). Alternatively, make the CSS minifier configurable so consumers can provide their own.

In bunchee's dist/index.js, the relevant code is:

javascript
const cleanCssInstance = new CleanCSS__default.default({});

function minify(code) {
  return cleanCssInstance.minify(code).styles;
}

This could be swapped to Lightning CSS:

javascript
import { transform } from 'lightningcss';

function minify(code) {
  const { code: minified } = transform({
    filename: 'style.css',
    code: Buffer.from(code),
    minify: true,
  });
  return minified.toString();
}

Lightning CSS has full support for native CSS nesting and is significantly faster than clean-css.

Alternatives Considered

  • Pre-flattening nesting with PostCSS/Tailwind before the build: This works for styles that go through a dedicated CSS compilation step (e.g. Tailwind's --minify flattens nesting), but doesn't help component-level CSS files that are imported directly in components and processed by bunchee's inlineCss plugin.
  • Disabling CSS minification entirely in bunchee: Avoids the corruption but sacrifices minification for all CSS.
  • Avoiding native CSS nesting in component CSS files: A workaround, but native CSS nesting is a stable browser feature (supported in all major browsers since Dec 2023) and developers should be able to use it.

Who Benefits?

Any library author using tsdx/bunchee who wants to use native CSS nesting in component-level CSS files. As CSS nesting adoption grows (it's now baseline across all major browsers), this will affect an increasing number of users.

Additional Context

  • clean-css v5.x is effectively in maintenance mode and has no plans to add nesting support — the tokenizer's Level system would require a significant architectural rewrite.
  • bunchee v6.9.4 pins clean-css: ^5.3.3 as a direct runtime dependency.
  • tsdx v2.0.0 delegates entirely to bunchee via execa("bunchee").
  • The issue is silent — clean-css produces corrupted output without any errors or warnings, making it difficult to diagnose.