Files
moeka-project/.agents/skills/tsdown/references/option-css.md
T

6.9 KiB

CSS Support

Status: Experimental — API and behavior may change.

Configure CSS handling including preprocessors, syntax lowering, minification, and code splitting.

Getting Started

All CSS support in tsdown is provided by the @tsdown/css package. Install it to enable CSS handling:

npm install -D @tsdown/css

When @tsdown/css is installed, CSS processing is automatically enabled. Without it, encountering CSS files will result in an error.

CSS Import

Import .css files from TypeScript/JavaScript — CSS is extracted into separate .css assets:

// src/index.ts
import './style.css'
export function greet() { return 'Hello' }

Output: index.mjs + index.css

@import Inlining

CSS @import statements are resolved and inlined automatically. No separate output files produced.

Inline CSS (?inline)

Append ?inline to return processed CSS as a JS string instead of emitting a .css file:

import './style.css'                   // → .css file
import css from './theme.css?inline'   // → JS string

Works with preprocessors too (./foo.scss?inline). Goes through full pipeline (preprocessors, @import inlining, lowering, minification). Tree-shakeable (moduleSideEffects: false).

CSS Pre-processors

Built-in support for Sass, Less, and Stylus. Install the preprocessor:

# Sass (either one)
npm install -D sass-embedded  # recommended, faster
npm install -D sass

# Less
npm install -D less

# Stylus
npm install -D stylus

Then import directly:

import './style.scss'
import './theme.less'
import './global.styl'

Preprocessor Options

export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `$brand-color: #ff7e17;`,
      },
      less: {
        math: 'always',
      },
      stylus: {
        define: { '$brand-color': '#ff7e17' },
      },
    },
  },
})

additionalData

Inject code at the beginning of every preprocessor file:

// String form
scss: {
  additionalData: `@use "src/styles/variables" as *;`,
}

// Function form
scss: {
  additionalData: (source, filename) => {
    if (filename.includes('theme')) return source
    return `@use "src/styles/variables" as *;\n${source}`
  },
}

CSS Minification

export default defineConfig({
  css: {
    minify: true,
  },
})

Powered by Lightning CSS.

CSS Target

Override the top-level target specifically for CSS:

export default defineConfig({
  target: 'node18',
  css: {
    target: 'chrome90', // CSS-specific target
  },
})

Set css.target: false to disable CSS syntax lowering entirely.

CSS Transformer

css.transformer controls mutually exclusive CSS processing paths:

  • 'lightningcss' (default): @import via Lightning CSS bundleAsync(), no PostCSS.
  • 'postcss': @import via postcss-import, PostCSS plugins applied, Lightning CSS for final transform only.
export default defineConfig({
  css: {
    transformer: 'postcss',
  },
})

PostCSS Options

export default defineConfig({
  css: {
    transformer: 'postcss',
    postcss: {
      plugins: [require('autoprefixer')],
    },
    // Or: postcss: './config' — path to search for postcss.config.js
  },
})

Auto-detects PostCSS config from project root when transformer is 'postcss' and css.postcss is omitted.

Lightning CSS (Syntax Lowering)

Install lightningcss to enable CSS syntax lowering based on your target:

npm install -D lightningcss

When target is set (e.g., target: 'chrome108'), modern CSS features are automatically downleveled:

/* Input */
.foo { & .bar { color: red } }

/* Output (chrome108) */
.foo .bar { color: red }

Custom Lightning CSS Options

import { Features } from 'lightningcss'

export default defineConfig({
  css: {
    lightningcss: {
      targets: { chrome: 100 << 16 },
      include: Features.Nesting,
    },
  },
})

css.lightningcss.targets takes precedence over both target and css.target for CSS.

CSS Modules

Files with .module.css (and .module.scss, .module.less, etc.) are treated as CSS modules — class names are scoped and exported as JS:

import styles from './app.module.css'
console.log(styles.title) // "scoped_title_hash"

Configuration

export default defineConfig({
  css: {
    modules: {
      scopeBehaviour: 'local',             // 'local' (default) | 'global'
      generateScopedName: '[hash]_[local]', // Lightning CSS pattern string
      localsConvention: 'camelCase',       // 'camelCase' | 'camelCaseOnly' | 'dashes' | 'dashesOnly'
    },
  },
})

Set css.modules: false to disable. Function-form generateScopedName requires transformer: 'postcss'.

Optional Dependencies (PostCSS path)

npm install -D postcss postcss-modules

Code Splitting

Merged (Default)

All CSS merged into a single file (default: style.css).

export default defineConfig({
  css: {
    fileName: 'my-library.css', // Custom name (default: 'style.css')
  },
})

Per-Chunk Splitting

export default defineConfig({
  css: {
    splitting: true, // Each JS chunk gets a corresponding .css file
  },
})

Preserving CSS Imports (css.inject)

When enabled, JS output preserves import statements pointing to emitted CSS files. Consumers auto-import CSS alongside JS:

export default defineConfig({
  css: {
    inject: true,
  },
})

PostCSS Optional Peer Dependencies

When using transformer: 'postcss', install these as needed:

Package Purpose Required When
postcss Core PostCSS engine Always (with transformer: 'postcss')
postcss-import Resolve/inline @import CSS uses @import
postcss-modules CSS modules (scoped classes) Using .module.css files
npm install -D postcss postcss-import postcss-modules

All declared as optional peer dependencies of @tsdown/css.

Options Reference

Option Type Default Description
css.transformer 'postcss' | 'lightningcss' 'lightningcss' CSS processing pipeline
css.splitting boolean false Per-chunk CSS splitting
css.fileName string 'style.css' Merged CSS file name
css.minify boolean false CSS minification
css.modules object | false {} CSS modules config, or false to disable
css.inject boolean false Preserve CSS imports in JS output
css.target string | string[] | false from target CSS-specific lowering target
css.postcss string | object PostCSS config path or inline options
css.preprocessorOptions object Preprocessor options
css.lightningcss object Lightning CSS options