# Lightning CSS: a Rust CSS parser, transformer and minifier with a real CLI

> Lightning CSS is a Rust CSS parser, transformer, bundler and minifier that ships as a native CLI, a Node package and a Rust crate. The interesting part is not the speed claim, it is that property values are parsed against the CSS grammar instead of passed around as token lists.

**parcel-bundler/lightningcss** — An extremely fast CSS parser, transformer, bundler, and minifier written in Rust.

- Repository: https://github.com/parcel-bundler/lightningcss
- Website: https://lightningcss.dev
- Stars: 7,690 · Forks: 306
- Language: Rust
- License: MPL-2.0
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/parcel-bundler-lightningcss

## The token-stream problem Lightning CSS refuses to inherit

Most CSS tooling treats a declaration value as an untyped series of tokens. The README states this directly: many other CSS parsers leave values as tokens, so every transformer that wants to act on a value has to interpret it again, which duplicates work and produces inconsistencies between plugins. Lightning CSS takes the other route. It parses values using the grammar from the CSS specification and exposes a specific value type for each property.

That choice is what makes the rest of the feature list possible. Combining longhand properties into shorthands, normalizing property value order, reducing calc() expressions and converting colors to shorter hex notation all require knowing what the value means, not just where it starts and ends. The audience is build-tool authors and teams that maintain a CSS pipeline rather than individual stylesheets. If you only ever concatenate files, the typed value model buys you nothing.

## cssparser and selectors underneath, Lightning CSS on top

The parsing foundation is not written from scratch. The README says Lightning CSS is built on the cssparser and selectors crates created by Mozilla and used by Firefox and Servo, and that Lightning CSS implements support for specific CSS rules and properties on top of that base. The Cargo.toml confirms the dependency layout: cssparser 0.37.0 and a local path dependency named parcel_selectors at version 0.28.3, with the selectors crate vendored into the repository as its own workspace member.

The workspace also lists node, napi, c, derive, static-self and static-self-derive as members, which maps to the delivery surface: a Rust library, a Node binding through napi, and a C API. Features are gated rather than always compiled. The default feature set is bundler, nodejs and sourcemap; the cli feature pulls in clap, serde_json, browserslist and jemallocator; a separate browserslist feature enables browserslist-rs. That means a Rust consumer who only wants parsing and minification can turn off the bundler and the Node layer instead of paying for them.

## Building the CLI and installing the Node package

The repository exposes a binary target named lightningcss at src/main.rs, gated behind the cli feature. The README does not list install commands inline; it points to the project website for documentation, and the Cargo.toml declares the bin target together with the feature it requires.

```toml
[[bin]]
name = "lightningcss"
path = "src/main.rs"
required-features = ["cli"]
```

That block is the whole contract for the CLI: the binary is named lightningcss, its entry point is src/main.rs, and it is only built when the cli feature is on. The README describes the tool as usable as a standalone CLI, but it does not print a flag reference, so check the binary's own help output before scripting it.

On the JavaScript side the package is named lightningcss and the package.json declares main as node/index.js with types at node/index.d.ts, plus separate import and require export conditions. The engines field requires Node 12 or newer.

```json
{
  "name": "lightningcss",
  "main": "node/index.js",
  "types": "node/index.d.ts",
  "engines": {
    "node": ">= 12.0.0"
  }
}
```

The package depends on detect-libc, which is how it picks the right prebuilt native binary for the platform. The related searches include platform-specific strings such as lightningcss linux x64 gnu node and lightningcss darwin arm64 node, which is the shape you expect from a package that resolves a native artifact at install time. When no matching binary is available, that resolution is the step to inspect first.

## Targets decide prefixing and syntax lowering

Vendor prefixing here is not a static list. The README says Lightning CSS accepts a list of browser targets and automatically adds and removes vendor prefixes based on them. The same targets drive syntax lowering: modern CSS is parsed and more compatible output is generated where needed. The documented lowering set includes CSS Nesting, custom media queries, logical properties, color-mix(), relative color syntax, lab() and oklch() and the other Color Level 4 functions, the :is and :dir and multi-argument :not selectors, clamp() and round() and rem() and mod(), alignment shorthands, two-value overflow, media query range syntax, multi-value display, and system-ui font family fallbacks.

Browserslist discovery is opt-in, per the README, so existing configuration can be reused rather than duplicated. The package.json itself carries a browserslist field reading last 2 versions, not dead, which is the project's own default for its build. The consequence for adopters is that output is a function of the target list. Change the targets and the emitted CSS changes, including prefix removal. A pipeline that pins targets in one place and lets a second tool guess them elsewhere will produce two different stylesheets.

## What minification actually rewrites

The minifier is described as one of the main purposes of the project, and the README enumerates the optimizations: combining longhand properties into shorthands where possible, merging adjacent rules with the same selectors or declarations when it is safe, combining CSS transforms into a single matrix or the reverse when smaller, removing vendor prefixes not needed for the given targets, reducing calc() expressions, converting colors to shorter hex, minifying gradients and CSS grid templates, normalizing property value order, dropping default sub-values browsers infer, and micro-optimizations such as shorter units and removing unnecessary quotation marks.

The README makes a claim worth reading carefully before comparing byte counts. It notes that some tools shown in its benchmark comparison perform unsafe optimizations that may change the behavior of the original CSS in favor of smaller file size, and states that Lightning CSS does not do this, with output CSS that should always behave identically to the input. That is a deliberate trade: you give up some byte savings in exchange for not debugging a layout that changed because a minifier merged two rules it should not have. The published benchmark output in the README shows lightningcss finishing bootstrap-4.css in 4.16ms at 143091 bytes, animate.css in 1.973ms at 23666 bytes, and tailwind.css in 43.368ms at 1824130 bytes, against cssnano and esbuild numbers in the same block. Those are the project's own figures on its own inputs, not a general claim about your stylesheet.

## CSS modules, custom transforms, and where the plugin story stops

Lightning CSS compiles a subset of CSS modules, and the README is explicit that it is a subset. The supported pieces are locally scoped class and id selectors, locally scoped custom identifiers such as @keyframes names, grid lines and areas and @counter-style names, opt-in scoping for CSS variables and other dashed identifiers, the :local() and :global() selectors, and the composes property. Anything outside that list is not covered by the documented CSS modules support.

Custom transforms go through the visitor API rather than a plugin registry. The README describes the visitor API as the way to implement custom transform plugins, and the Cargo.toml exposes a visitor feature plus a substitute_variables feature that depends on visitor and into_owned. The examples directory contains custom_at_rule.rs, schema.rs and serialize.rs, which is where a Rust integrator should look first.

This is the clearest limitation. A PostCSS user arrives with a large ecosystem of published plugins and expects to keep them. Here, custom behavior means writing Rust against the visitor API, or running Lightning CSS as one step inside a tool that already has a plugin system. The related searches include Postcss lightningcss and Lightningcss plugins, which suggests people hit exactly this boundary. If your build depends on several PostCSS plugins that manipulate declarations semantically, Lightning CSS is the wrong replacement for the whole chain; it can still be the minifier at the end of it.

## How it differs from esbuild, PostCSS and Sass

The difference against esbuild is scope. The README's own benchmark block runs both tools on the same three files, and esbuild appears in the repository as a comparison point in bench.js. esbuild is a general bundler that also minifies CSS; Lightning CSS is a CSS-specific parser and transformer that can be embedded in a bundler. The typed value model is the concrete distinction: a general-purpose bundler does not parse each property value against the CSS grammar, so it cannot safely combine longhands into shorthands or normalize value order.

Against PostCSS the difference is the extension mechanism and the language. PostCSS is a JavaScript tool with a plugin ecosystem; Lightning CSS is a Rust library with Node bindings and a visitor API. Against Sass the difference is category. Sass is a preprocessor with its own syntax and language features; Lightning CSS parses standard CSS and lowers modern syntax for older targets. The related searches list lightning css vs sass and Lightningcss Sass, and the honest answer from the README is that Sass compilation and CSS lowering are separate jobs, so a project can run both. The same applies to lightning css vs tailwind css: Tailwind generates utility CSS, Lightning CSS parses and minifies whatever CSS it is given.

## Conclusion

Adopt Lightning CSS when you need typed property values, prefixing driven by browser targets, and syntax lowering in one pass, and you are willing to keep a native binary in your build. Do not adopt it if you depend on the full PostCSS plugin ecosystem or on a JavaScript-only toolchain, because the plugin surface here is a visitor API rather than a package registry. Before committing, run the CLI on one of your own stylesheets and diff the output against your current minifier, then check that the targets you pass match the browsers you actually support.

## FAQ

### What is Lightning CSS?

It is a CSS parser, transformer, bundler and minifier written in Rust, usable from Parcel, as a standalone library from JavaScript or Rust, or through a standalone CLI. The README describes it as built on the cssparser and selectors crates from Mozilla.

### How does Lightning CSS compare with PostCSS?

PostCSS is a JavaScript tool whose behavior is extended through plugins, while Lightning CSS is a Rust library with Node bindings and a visitor API for custom transforms. Lightning CSS parses property values against the CSS grammar and exposes a specific value type per property, which the README contrasts with parsers that keep values as untyped tokens.

### What is the difference between Lightning CSS and Sass?

Sass is a preprocessor with its own language features, and Lightning CSS parses standard CSS and lowers modern syntax such as CSS Nesting and color-mix() to more compatible output based on browser targets. The README does not present Lightning CSS as a Sass replacement, so the two can run in the same pipeline.

### What is the difference between Lightning CSS and Tailwind CSS?

Tailwind CSS generates utility stylesheets, and Lightning CSS parses, transforms and minifies CSS that it is given. The README lists minification and syntax lowering as the project's purposes, not class generation.

### What alternative is there to Lightning CSS?

The README's benchmark block compares it against cssnano and esbuild and notes that some tools perform unsafe optimizations that may change the behavior of the original CSS, which Lightning CSS states it does not do. Which one fits depends on whether you need typed property values and target-driven prefixing, or a JavaScript plugin ecosystem.

## Sources

- [License: MPL-2.0](https://github.com/parcel-bundler/lightningcss/blob/master/LICENSE)
- [parcel-bundler/lightningcss on GitHub](https://github.com/parcel-bundler/lightningcss)
- [Project website](https://lightningcss.dev)
- [README](https://github.com/parcel-bundler/lightningcss/blob/master/README.md)
- [Releases](https://github.com/parcel-bundler/lightningcss/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/parcel-bundler-lightningcss
