mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
### What?
Adds a new `experimental.lightningCssFeatures` config option that lets
users control which CSS features lightningcss should always transpile
(`include`) or never transpile (`exclude`), regardless of browserslist
targets.
```js
// next.config.js
module.exports = {
experimental: {
useLightningcss: true,
lightningCssFeatures: {
include: ['light-dark', 'oklab-colors'],
exclude: ['nesting'],
},
},
}
```
### Why?
Currently, lightningcss feature transpilation is determined solely by
browserslist targets. There's no way to force transpilation of a
specific feature (e.g., `light-dark()`) when targeting modern browsers
that already support it, or to skip transpilation of a feature that the
user wants to preserve as-is.
This is useful for:
- Forcing polyfill-style transpilation for features with incomplete
browser support
- Testing transpiled output for specific features
- Opting out of specific transforms that may interfere with CSS tooling
downstream
### How?
**TypeScript config & validation:**
- `config-shared.ts`: `LIGHTNINGCSS_FEATURE_NAMES` const array (single
source of truth) with `LightningCssFeature` type derived from it, and
`LightningCssFeatures` interface
- `config-schema.ts`: Zod validation using
`z.enum(LIGHTNINGCSS_FEATURE_NAMES)` for both include/exclude arrays
**Feature name → bitmask mapping (Rust, shared via NAPI):**
- `crates/next-core/src/next_config.rs`:
`lightningcss_feature_names_to_mask()` maps feature name strings to
`lightningcss::targets::Features` bitflag constants and returns a
`Result<u32>` bitmask. Unknown feature names produce an error via
`bail!`.
- Exposed to JS via NAPI as `lightningcssFeatureNamesToMaskNapi`,
callable through `bindings.css.lightning.featureNamesToMask(names)`
- Single source of truth for feature name resolution — both webpack and
Turbopack paths use this Rust function
**Webpack path:**
- `global.ts` / `modules.ts` pass config through to loader options
- `loader.ts` calls `featureNamesToMask()` via the native bindings to
compute include/exclude masks, then passes them to the SWC `transform()`
call
- The `lightningCssFeatures` without `useLightningcss` warning only
shows when using webpack (Turbopack always uses lightningcss)
**Turbopack path (Rust):**
- `next_config.rs`: `LightningCssFeatures` struct for deserialization,
accessor methods converting names → u32 bitmask via
`lightningcss_feature_names_to_mask()`
- Config flows through `CssOptionsContext` → `ModuleType::Css` →
`CssModuleAsset` (using `LightningCssFeatureFlags { include, exclude }`
struct) → `process.rs` where bitmasks are merged into
`lightningcss::targets::Targets { include, exclude }`
- Uses `u32` bitmask representation throughout to avoid adding
lightningcss dependency to non-CSS crates
**Defaults preserved:** `Nesting | MediaRangeSyntax` for Turbopack,
`Nesting` for Webpack. User `include` is OR-ed on top, user `exclude`
masks bits off.
**Feature names:** 21 individual features (bit 0–20) + 3 composite
groups (`selectors`, `media-queries`, `colors`), all in dash-case.
### Test
- **Include test**
(`test/e2e/app-dir/experimental-lightningcss-features/`) — targets
Chrome 123 (which natively supports `light-dark()`) with `include:
['light-dark']`, then asserts the CSS output contains lightningcss
transpilation markers (`--lightningcss-light`, `--lightningcss-dark`)
instead of raw `light-dark()`.
- **Exclude test**
(`test/e2e/app-dir/experimental-lightningcss-features-exclude/`) —
targets Chrome 100 (which does NOT support `light-dark()` natively) with
`exclude: ['light-dark']`, then asserts the CSS output preserves raw
`light-dark()` calls and does not contain transpilation markers.
127 lines
6.7 KiB
Plaintext
127 lines
6.7 KiB
Plaintext
---
|
|
title: useLightningcss
|
|
description: Enable experimental support for Lightning CSS.
|
|
version: experimental
|
|
---
|
|
|
|
Experimental support for using [Lightning CSS](https://lightningcss.dev) with webpack. Lightning CSS is a fast CSS transformer and minifier, written in Rust.
|
|
|
|
If this option is not set, Next.js on webpack uses [PostCSS](https://postcss.org/) with [`postcss-preset-env`](https://www.npmjs.com/package/postcss-preset-env) by default.
|
|
|
|
Turbopack uses Lightning CSS by default since Next 14.2. This configuration option has no effect on Turbopack. Turbopack always uses Lightning CSS.
|
|
|
|
```ts filename="next.config.ts" switcher
|
|
import type { NextConfig } from 'next'
|
|
|
|
const nextConfig: NextConfig = {
|
|
experimental: {
|
|
useLightningcss: false, // default, ignored on Turbopack
|
|
},
|
|
}
|
|
|
|
export default nextConfig
|
|
```
|
|
|
|
```js filename="next.config.js" switcher
|
|
/** @type {import('next').NextConfig} */
|
|
const nextConfig = {
|
|
experimental: {
|
|
useLightningcss: true, // disables PostCSS on webpack
|
|
},
|
|
}
|
|
|
|
module.exports = nextConfig
|
|
```
|
|
|
|
## `lightningCssFeatures`
|
|
|
|
By default, Lightning CSS decides which CSS features to transpile based on your [browserslist](https://browsersl.ist/) targets. The `lightningCssFeatures` option lets you override this by forcing specific features to always be transpiled (`include`) or never be transpiled (`exclude`), regardless of browser support.
|
|
|
|
This applies to both webpack (when `useLightningcss` is enabled) and Turbopack.
|
|
|
|
```ts filename="next.config.ts" switcher
|
|
import type { NextConfig } from 'next'
|
|
|
|
const nextConfig: NextConfig = {
|
|
experimental: {
|
|
useLightningcss: true,
|
|
lightningCssFeatures: {
|
|
// Always transpile these features, even if targets support them
|
|
include: ['light-dark', 'oklab-colors'],
|
|
// Never transpile these features, even if targets don't support them
|
|
exclude: ['nesting'],
|
|
},
|
|
},
|
|
}
|
|
|
|
export default nextConfig
|
|
```
|
|
|
|
```js filename="next.config.js" switcher
|
|
/** @type {import('next').NextConfig} */
|
|
const nextConfig = {
|
|
experimental: {
|
|
useLightningcss: true,
|
|
lightningCssFeatures: {
|
|
// Always transpile these features, even if targets support them
|
|
include: ['light-dark', 'oklab-colors'],
|
|
// Never transpile these features, even if targets don't support them
|
|
exclude: ['nesting'],
|
|
},
|
|
},
|
|
}
|
|
|
|
module.exports = nextConfig
|
|
```
|
|
|
|
### Options
|
|
|
|
| Option | Type | Description |
|
|
| --------- | ---------- | -------------------------------------------------------------------------- |
|
|
| `include` | `string[]` | Features to always transpile, regardless of browser targets. |
|
|
| `exclude` | `string[]` | Features to never transpile, even when browser targets would require them. |
|
|
|
|
### Available features
|
|
|
|
Individual features:
|
|
|
|
| Feature name | Description |
|
|
| ----------------------------------- | ---------------------------------------------------- |
|
|
| `nesting` | [CSS Nesting](https://drafts.csswg.org/css-nesting/) |
|
|
| `not-selector-list` | `:not` with multiple selectors |
|
|
| `dir-selector` | `:dir()` selector |
|
|
| `lang-selector-list` | `:lang()` with multiple languages |
|
|
| `is-selector` | `:is()` selector |
|
|
| `text-decoration-thickness-percent` | Percentage values in `text-decoration-thickness` |
|
|
| `media-interval-syntax` | Media query range interval syntax |
|
|
| `media-range-syntax` | Media query range syntax (`width >= 600px`) |
|
|
| `custom-media-queries` | `@custom-media` rules |
|
|
| `clamp-function` | `clamp()` function |
|
|
| `color-function` | `color()` function |
|
|
| `oklab-colors` | `oklab()` and `oklch()` colors |
|
|
| `lab-colors` | `lab()` and `lch()` colors |
|
|
| `p3-colors` | Display P3 colors |
|
|
| `hex-alpha-colors` | 4 and 8 digit hex colors with alpha |
|
|
| `space-separated-color-notation` | Space-separated color notation (`rgb(0 0 0)`) |
|
|
| `font-family-system-ui` | `system-ui` font family |
|
|
| `double-position-gradients` | Double-position gradient stops |
|
|
| `vendor-prefixes` | Vendor-prefixed properties and values |
|
|
| `logical-properties` | Logical properties and values |
|
|
| `light-dark` | `light-dark()` color function |
|
|
|
|
Composite groups (shorthand for enabling multiple features at once):
|
|
|
|
| Group name | Includes |
|
|
| --------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
| `selectors` | `nesting`, `not-selector-list`, `dir-selector`, `lang-selector-list`, `is-selector` |
|
|
| `media-queries` | `media-interval-syntax`, `media-range-syntax`, `custom-media-queries` |
|
|
| `colors` | `color-function`, `oklab-colors`, `lab-colors`, `p3-colors`, `hex-alpha-colors`, `space-separated-color-notation`, `light-dark` |
|
|
|
|
## Version History
|
|
|
|
| Version | Changes |
|
|
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
| `16.2.0` | `lightningCssFeatures` added. |
|
|
| `15.1.0` | Support for `useSwcCss` was removed from Turbopack. |
|
|
| `14.2.0` | Turbopack's default CSS processor was changed from `@swc/css` to Lightning CSS. `useLightningcss` became ignored on Turbopack, and a legacy `experimental.turbo.useSwcCss` option was added. |
|