From lightningcss¶
lightningcss binds the Rust CSS engine behind Parcel to Python through
process_stylesheet. It is a cascade-aware optimizer, not just a minifier: alongside whitespace and value compaction
it drops declarations overridden elsewhere in the sheet, merges rules, adds or strips vendor prefixes, and rewrites
syntax down to a configured browser-target set, so it can reach a smaller result than a value-safe minifier can. That
target-driven approach makes it the CSS transform stage in Parcel and a common build-time optimizer for web bundles.
turbohtml covers the same ground with turbohtml.clean.minify_css(), a value-safe minifier. It applies only
transforms that hold on any conformant browser, so its default output needs no target list, parses to the same cascade
everywhere, and recovers from malformed input that lightningcss rejects. The CSSMinify
baseline year opts into newer-syntax shorthand merges when you are ready to require them.
turbohtml vs lightningcss¶
Dimension |
turbohtml |
lightningcss |
|---|---|---|
Scope |
Value-safe CSS minify (full sheet and inline declarations) |
Cascade-aware CSS optimizer: minify, prefix, bundle, CSS modules |
Feature breadth |
Whitespace + value compaction, shortest colors, unit/number trimming, constant |
All of that plus cross-sheet dead-declaration removal, target-driven syntax rewriting, vendor prefixing,
|
Performance |
Faster on some sheets (up to 2.3x), behind the Rust engine on the largest (see Performance) |
Smaller output on most inputs by trading target configuration for size |
Typing |
Fully typed, frozen |
Typed bindings over the Rust engine |
Dependencies |
Native C extension in turbohtml, no extra dependency |
Native Rust extension |
Maintenance |
Maintained within turbohtml |
Tracks the Parcel/lightningcss Rust project |
Feature overlap¶
The minify surface ports 1:1:
lightningcss.process_stylesheet(css, minify=True)->turbohtml.clean.minify_css().Whitespace collapse, shortest hex/color forms, redundant unit and leading-zero trimming, and constant
calc()folding are applied by both.Adjacent-rule and shorthand merging: turbohtml merges the value-safe cases by default and the newer-syntax cases (
inset, flexgap, two-valueoverflow) when you setbaseline.
What turbohtml adds¶
Value-safe by construction: every transform round-trips to the same cascade on any conformant browser, so there is no target list to configure and no way to emit syntax a target cannot parse.
WHATWG error recovery: turbohtml minifies malformed stylesheets that lightningcss’s parser rejects, for example a media query in
foundation.cssthat the WHATWG rules accept.Faster on some sheets: up to 2.3x on
animate.cssand ahead onpico.css, though the Rust engine leads on the largest inputs.Inline-declaration minify via
turbohtml.clean.minify_css_inline()for barestyle-attribute declaration lists, without a surrounding selector or braces.Custom-property values and string contents stay byte-exact, as CSS Variables 1 requires.
What lightningcss has that turbohtml does not¶
Cross-sheet cascade optimization: lightningcss removes declarations overridden elsewhere in the sheet. turbohtml does not, because the result depends on which browsers you support; there is no value-safe equivalent.
Target-driven syntax rewriting to a browser-target set. turbohtml’s
baselinegates newer-syntax shorthand merges but does not rewrite down to an arbitrary older-target set.Vendor prefixing (adding or removing prefixes for a target). No equivalent; turbohtml preserves the input’s prefixes.
@importbundling and CSS-modules transforms. No equivalent; turbohtml minifies a single stylesheet in place.
Performance¶
lightningcss produces the smaller output on most of the corpus – up to seven percent under turbohtml on animate.css
– because it optimizes for a browser-target set: it removes declarations overridden across the sheet and emits syntax
those targets support. turbohtml applies only transforms that hold on any conformant browser, so its default output
needs no target list and parses to the same cascade everywhere; the CSSMinify baseline
year opts into the newer-syntax shorthand merges when you are ready to require them. On speed the two trade places:
turbohtml is 2.3x faster on animate.css and edges ahead on pico.css, while lightningcss’s Rust engine leads on
the larger bootstrap.css and bulma.css. turbohtml recovers from malformed CSS that lightningcss rejects:
foundation.css raises a parse error on a media query the WHATWG rules accept, where turbohtml minifies all six
stylesheets. Each ratio is against turbohtml:
stylesheet |
turbohtml |
|||
|---|---|---|---|---|
size |
time |
size |
time |
|
normalize.css (6 kB) |
1.8 kB |
55 µs |
1.8 kB (1.001x) |
45.9 µs (0.9x) |
pico.css (90 kB) |
81.2 kB |
1.43 ms |
80.0 kB (0.99x) |
1.51 ms (1.1x) |
animate.css (93 kB) |
73.8 kB |
647 µs |
68.8 kB (0.93x) |
1.46 ms (2.3x) |
bootstrap.css (274 kB) |
229.6 kB |
6.51 ms |
228.7 kB (0.996x) |
4.42 ms (0.7x) |
bulma.css (745 kB) |
681.4 kB |
18.8 ms |
674.3 kB (0.99x) |
11.5 ms (0.7x) |
Reach for lightningcss when you can pin a browser-target set and want the last few percent of size; reach for turbohtml when you want value-safe output with no configuration and tolerance of real-world CSS.
How to migrate¶
The import and the call are the only change:
# lightningcss
from lightningcss import process_stylesheet
process_stylesheet(css, minify=True)
# turbohtml
from turbohtml.clean import minify_css
minify_css(css)
lightningcss |
turbohtml |
|---|---|
|
|
|
|
No equivalent (full-sheet input only) |
|
from turbohtml.clean import minify_css
print(minify_css("a {\n color: #ffffff;\n margin: 0px 0px;\n}"))
a{color:#fff;margin:0}
To target a newer browser baseline, pass CSSMinify; the year gates the newer-syntax merges,
much as lightningcss’s targets gate its own:
from turbohtml.clean import CSSMinify, minify_css
print(minify_css("a{top:0;right:0;bottom:0;left:0}", CSSMinify(baseline=2021)))
a{inset:0}
Gotchas and pitfalls¶
lightningcss removes declarations overridden elsewhere in the sheet and rewrites syntax for its targets; turbohtml does neither by default, because both depend on which browsers you support. Set
baselineto recover the newer-syntax merges, but the whole-sheet cascade optimization has no value-safe equivalent.lightningcss aborts on a stylesheet its parser rejects; turbohtml follows the WHATWG error-recovery rules, so it minifies inputs like
foundation.cssthat lightningcss will not parse.lightningcss can add or drop vendor prefixes for a target; turbohtml preserves the input’s prefixes as written.
turbohtml keeps custom-property values and string contents byte-exact; do not expect the internal whitespace of a
var()value or a string literal to be collapsed.Both are native extensions (Rust versus C), so neither offers a pure-Python fallback.