NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #1992 most downloaded on npm
Merge CSS rules with PostCSS.
Last release today
05 Oct 2026
Release timing varies
gaps range from 8 days to 4 months
Most releases are documented
notes for 53 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
12 years old
103 releases · first in 2015
fix: collapse redundant whitespace in at-rules in #1999
Full Changelog: https://github.com/cssnano/cssnano/compare/cssnano@9.1.1...cssnano@9.1.2
fix(postcss-convert-values): preserve unicode range in #1995
Full Changelog: https://github.com/cssnano/cssnano/compare/cssnano@9.1.0...cssnano@9.1.1
One column per quarter.
Merge corner border-*-radius longhands into border-radius shorthands. Four complete corner declarations in the same importance lane now collapse into
border-*-radius longhands into border-radius shorthands. Four complete corner declarations in the same importance lane now collapse into one shorthand, with horizontal and vertical axes minified independently and separated by a slash when distinct.Merge margin, padding, border, border-radius, columns, and border-spacing declarations inside at-rule containers such as @page, @position-try, and nested at-rules. Fold identical horizontal and vertical border-spacing components to a single value. Recognize columns shorthands combining a count and a math function in either order, and preserve invalid declarations as written: border shorthands with multiple styles and columns values combining CSS-wide keywords with widths or counts no longer discard preceding valid longhands.
Preserve percentage units in registered custom-property initial values with composite syntax, ranges, groups, and multipliers, and avoid clamping numeric operands nested inside alpha-value functions.
Restrict color minification to CSS properties that accept <color> and custom properties, preserving custom identifiers in properties like animation-name, grid-area, and counter-reset. Add support for hwb() color values and minification inside color-mix(), light-dark(), and var() fallbacks, while preserving relative color syntax, math functions, and token boundary separators.
Preserve zero percentage units in rgb(), rgba(), and modern CSS Color 4 functions to avoid invalid syntax, respect disabled time conversions for 0ms, clamps percentage opacity values adhering to CSS Color 4 alpha semantics, and improve compatibility with uppercase at-rules and vendor-prefixed keyframes.
Preserves zero length in columns shorthands and zero units inside @property with <angle-percentage> syntax, retaining keyframe percentage units under nested rules. Symmetrically rounds negative numbers when precision is configured, converts between metric units (mm, cm, q), preserves zero percentages on SVG stroke properties to retain transition interpolation, and caches Browserslist lookups across runs. Also preserves units inside CSS Fonts 5 override descriptors, anchor() functions, flex-basis, and IE-targeted sizing properties.
Remove comments more reliably and preserve the ones you keep exactly. Comments are now removed from !important declaration values and comment detection respect CSS token boundaries: text inside unquoted url() values and behind escape sequences in strings and identifiers is no longer mistaken for a comment. Kept comments are emitted byte-for-byte, including an unclosed comment at the end of a value, and whitespace inside kept comments is left untouched. Removal state is scoped to each document, so a shared processor no longer carries removeAllButFirst first-comment state between runs, and the remove callback runs once per comment occurrence. A non-function remove option now fails with a TypeError.
Improve conformance for modern CSS math functions, grid-line values, and case-sensitive attribute selector modifiers while preserving ambiguous or invalid declarations. The affected transforms now use bounded, linear scans for these forms.
Value transforms now read the CSS Values 4 math function names from one mathFunctions table in cssnano-utils, so unit retention, box and shorthand merging, parameter and whitespace handling, and time classification agree on which functions count as math functions. Serialized output is unchanged.
Standardize output normalization for margin, padding, and physical border properties. Property names and case-insensitive keywords in generated shorthands and normalized standalone shorthand declarations are canonicalized to lowercase, while preserving author casing for unmerged longhands, custom properties, hack prefixes, and unresolved values.
perf: median 15% speed increase on test fixtures
postcss-normalize-url now decodes quoted url(...) values, preserving literal backslashes and escape sequences whether quotes are stripped or kept.
Relative URLs now normalize using POSIX path semantics across all platforms, and converted @namespace URLs escape double quotes.
Join every escaped line continuation in a multiline url() value into one line, including \r\n continuations that previously left an invalid string behind. Rewriting a @namespace URL no longer overwrites the tokens that follow it, so trailing text is preserved and multiple URLs in one namespace declaration are each normalized.
Remove insignificant whitespace around custom-property names and, in standard declarations, around env() custom-ident arguments and around comma delimiters in var(), env(), and constant(). Between a custom-property name and its value, only the parser-consumed leading whitespace run is dropped; authored whitespace elsewhere in the value, such as after a preserved comment, is preserved per CSS Variables 2. Required whitespace between distinct tokens and the single whitespace token in an empty fallback remain preserved, and a preserved comment kept between a declaration name and value now survives minification, for custom properties and standard declarations alike. Comment removal in selectors and values no longer fuses the tokens the comment in-between: an attribute case-insensitivity flag or any other name-like token keeps its boundary, and math-operator spacing is restored for every function whose value productions accept <calc-sum> arguments (including calc-size(), calc-mix(), and random()). Values without comments are now left byte-for-byte untouched rather than re-spaced.
Preserve whitespace around division operators across all modern CSS math functions such as min(), max(), and clamp(), and trim extraneous whitespace from commas in nested calculations.
Order columns: 2 auto as columns: auto 2. Leave animation declarations with negative iteration counts unchanged. Reorders border and box-shadow math functions that resolve to a length, such as calc(1px + 1em), and leaves other math unchanged. Passes through unknown box-shadow color functions instead of reordering them.
Merge duplicate @keyframes and @counter-style definitions more conservatively.
Use ASCII case-insensitive matching for CSS names and grammar keywords so Unicode lookalikes are preserved, and restrict CSS whitespace normalization to the CSS whitespace set.
Rename identifiers only when their definition and a reference are in the same stylesheet; leave alone names inside var(), env(), attr(), or a custom property fallback; skip reserved words in the encoder; rename reversed() counters and implicit <area>-start/-end lines with their area.
Preserve Windows drive roots, directory dot references, and leading ./ prefixes when the first relative segment contains a colon. Decode unreserved percent-encoded octets while preserving percent-encoded dots, normalize @import at-rules including nested condition URLs, retain quotes for URLs containing non-printable control characters, avoid unnecessary declaration tokenization, and synchronize raw parameters.
Minify equal shorthand components for two-axis, four-side, alignment, aspect-ratio, and transition declarations while preserving grammar-sensitive and invalid values.
fix(postcss-minify-params): support CSS values level 4 and Media Queries level 5
Preserves invalid selector syntax and namespace-sensitive universal selectors while safely normalizing pseudo-element, View Transition, and keyframe forms. Recognizes the full token grammar for An+B formulas, including escaped forms and exact large integers.
fix(postcss-ordered-values): improve performance and correctness
Aligns SVG data URI processing with WHATWG URL standards by treating the first # as the fragment delimiter. Non-conforming data URLs (such as those with malformed percent-encoding or where an unencoded # in markup truncates the SVG payload) are left untouched rather than parsed with an internal XML recovery scanner. CSS hex escapes in the URI scheme (for example d\61ta:image/svg+xml) reach the optimizer. SVG data URIs inside CSS Values 4 src(...) functions or url(...) declarations with url-modifiers are optimized while preserving modifiers and comments. Minified declarations also keep their raw value metadata in sync.
Unify physical-border optimization under a single reducer that chooses deterministic shortest non-crossing canonical shorthands from complete side and component groups, partitioned by importance lane. Partial grids no longer require a full border reset. Dynamic declarations, style hacks, CSS-wide keywords, support fallbacks, unresolved substitutions, and other cascade barriers remain in their original positions, while valid fallback and border-image behavior is preserved.
Parse font weights and column units from tokenizer metadata. This safely minifies escaped bold weights, recognizes escaped length units, and prevents partial rewrites of invalid font shorthands while preserving original source spelling.
Update browserslist
Preserve matching-importance all reset boundaries when merging margin, padding, physical border-radius, and columns declarations, so minification does not restore values cleared by the reset.
Full Changelog: https://github.com/cssnano/cssnano/compare/cssnano@9.0.5...cssnano@9.1.0
@media, @supports and @container blocks with identical conditions even when other rules sit between them, when none of those rules sets a conflicting property.Do not merge rules across all, shorthands that reset their longhands, logical properties that can address the same side, and at-rules whose order matters, such as @layer and @import. Adjacent rules with the same selector now become one rule. Add the selectors of a later rule to an earlier rule with the identical declaration.Remove the enclosing named @layer and conditional blocks that moving a rule out of a nested conditional group rule left empty.
Do not merge nested rules when a conflicting declaration from the enclosing rule lies between them, so .p{&{color:red}color:blue;&{color:red}} stays red instead of blue.
Leave the keyframes of an uppercase @KEYFRAMES rule unmerged, as for @keyframes. At-rule names are case-insensitive, and merging keyframes repeats a keyframe selector.
Determine the vendor prefix of a selector only from its pseudo-class and pseudo-element names. A class name or attribute value such as .x-moz-y or [title=":-moz-x"] no longer blocks merging with a ::-moz-selection rule, and a selector that mixes two vendor prefixes is never merged. Compare pseudo-element names case-insensitively, so ::-WEBKIT-scrollbar now counts as WebKit-prefixed and stays apart from unprefixed rules.
Fix an infinite loop: when equal @media blocks were separated by an at-rule such as @layer x;, @font-face or @page, the plugin would keep merging a ru
Fix an infinite loop: when equal @media blocks were separated by an at-rule such as @layer x;, @font-face or @page, the plugin would keep merging a rule into itself until memory ran out.
Fix a bug when the plugin moved a rule into a block nested inside another block: a later merge could combine two rules in reverse order and change which declaration wins.
Stop merging rules that have an invalid selector, such as b: hover, [a b], :lang(en fr) or a||b. An invalid selector makes the whole selector list inv
Stop merging rules that have an invalid selector, such as b: hover, [a b], :lang(en fr) or a||b. An invalid selector makes the whole selector list invalid, so the browser also dropped the valid rule it was merged with.
Stop turning a comment inside a selector into a descendant combinator. A comment is not whitespace, so .a/**/.b selects elements with both classes, including inside :is(), and is no longer rewritten as .a .b. Invalid selectors such as div/**/span are left as written instead of becoming valid.
Require Browserslist 4.29.3 or later.
### Patch Changes - fix: update browserslist
fix(postcss-svgo): decode SVG following WHATWG specification in #1975
Full Changelog: https://github.com/cssnano/cssnano/compare/cssnano@9.0.4...cssnano@9.0.5
Rules with identical selectors now merge without changing which declaration wins the cascade. A rule that repeats a value already overridden inside the earlier rule, or that overrides a later shorthand with a longhand, keeps its declaration instead of being dropped as a duplicate, so the computed styles of merged rules match the original stylesheet.
Declarations whose standard property names differ only in case, such as COLOR and color, now merge like identically spelled properties. Custom property names stay case-sensitive, so --FOO and --foo remain distinct declarations.
Improve conformance for modern CSS math functions, grid-line values, and case-sensitive attribute selector modifiers while preserving ambiguous or invalid declarations. The affected transforms now use bounded, linear scans for these forms.
Merge rules that use modern pseudo-classes and pseudo-elements — :modal, ::file-selector-button, :read-only, :read-write, :autofill, and :fullscreen — when every browser in the target list supports the corresponding feature. Support is still checked per browser, so these selectors remain unmerged under older targets, and pseudos without verified support data such as :popover-open and :user-invalid continue to block merging.
perf: median 15% speed increase on test fixtures
Profitable adjacent rule merges are no longer skipped by the legacy look-ahead, and important comments are preserved when equal-declaration rules are merged.
Use ASCII case-insensitive matching for CSS names and grammar keywords so Unicode lookalikes are preserved, and restrict CSS whitespace normalization to the CSS whitespace set.
update browserslist
Updated dependencies:
fix: update postcss-calc in #1970
Full Changelog: https://github.com/cssnano/cssnano/compare/cssnano@9.0.3...cssnano@9.0.4
fix: update color parsing library in #1964
fix: update color parsing library in #1964
fix: update PostCSS and selector parser
fix: update conflict detection and color conversion CSS spec conformance
fix: update PostCSS peer dependency
Full Changelog: https://github.com/cssnano/cssnano/compare/cssnano@9.0.2...cssnano@9.0.3
fix: add main and types to package manifests in #1958
Full Changelog: https://github.com/cssnano/cssnano/compare/cssnano@9.0.1...cssnano@9.0.2
refactor: some small performance gains in #1955
Full Changelog: https://github.com/cssnano/cssnano/compare/cssnano@9.0.0...cssnano@9.0.1
Use deterministic greedy scheduling when considering safe adjacent rule merges. This can produce smaller valid CSS than the previous streaming order.
pef(stylehacks): avoid rescanning the nodes when checking for compatibility hacks
Migrate to ESM and simplify config loading in #1948
Probably backward-compatible for most users. Some changes to configuration loading documented in migration guide.
Full Changelog: https://github.com/cssnano/cssnano/compare/cssnano@8.0.10...cssnano@9.0.0
^22.22.3 || ^24.15.0 || >=26.0. Package subpath imports are no longer supported. See the migration documentation before upgrading.fix(postcss-merge-longhand): fix issues with border merging in #1886
Full Changelog: https://github.com/cssnano/cssnano/compare/cssnano@8.0.4...cssnano@8.0.5
perf(postcss-merge-rules): cache negative browser-support results when deciding whether to merge rules
Checking whether a selector feature is supported by the target browsers previously skipped the cache whenever the feature was not supported, so every occurrence of an unsupported feature re-ran the browserslist/caniuse lookup. This is now cached, which speeds up rule merging.
Fix postcs-svgo encoding in #1877
Full Changelog: https://github.com/cssnano/cssnano/compare/cssnano@8.0.3...cssnano@8.0.4
fix(postcss-merge-rules): keep the cascade intact when hoisting declarations
fix(postcss-merge-rules): decide property conflicts from spec data
The plugin decides whether to reorder declarations using @webref/css data instead of a name-based heuristic. For example, the plugin now recognizes that font and line-height, border-width and border-left, gap and row-gap, and inset and top conflict, and that a flow-relative property and its physical counterpart override each other.
The plugin now merges properties that do not override each other, but only share part of the name, such as flexandflex-direction`. Vendor extensions that are not part of a W3C specification still fall back to comparing names.
fix: ensure packages reach registry with correct repository field
fix(postcss-merge-rules): merge interleaved declarations correctly
postcss-reduce-initial: update initial properties
packages now define an exports field in package.json. This prevents any file added to the package to automatically be exposed as a possible import. The has been done in a backward compatible way, so you should be able to import every file you did in the previous release.
types have also been regenerated with TypeScript 7.
Full Changelog: https://github.com/cssnano/cssnano/compare/cssnano@8.0.2...cssnano@8.0.3
chore: regenerate all type definitions with TypeScript 7
chore: regenerate all type definitions with TypeScript 7
fix: update svgo, autoprefixer and postcss
feat: make some packages runnable on the web without extra polyfills
fix: update PostCSS
chore: define package.json exports
chore: update dependencies
Update autoprefixer, browserslist, colordx and postcss
chore: update the postcss peer dependency
ea8e33a: chore: drop Node.js 20 support
ea8e33a: chore: drop Node.js 20 support
Node.js 20 has reached end of life.
### Patch Changes - 7e56dba: fix: update postcss - Updated dependencies [7e56dba] - cssnano-utils@5.0.3
322ad33: fix: update postcss peer dependency
5cb8b09: fix: udpate browserslist
c3e537a: fix: update postcss-selector-parser
72dd9c9: fix: update browserslist
906a785: fix: update browserslist
5672148: fix: update PostCSS peer dependency to version without vulnerabilities
1d65a10: fix: update postcss-selector-parser
dff5c42: chore: update browserslist and postcss-selector-parser
0c85fa9: fix: update Browserslist version
c14b9f5: chore: update postcss-selector-parser
0d10597: chore: drop support for Node.js 14 and 16
0856f86: chore: update postcss-selector-parser
feat: add preset and plugin options for browserslist
83d3268: chore: update autoprefixer and browerslist
f233b22: chore: update-postcss-selector-parser to 6.0.14
18331a6: fix: update cssnano peer dependency to 8.4.31 to avoid security issue
fix(postcss-merge-rules): do not merge nested rules
ca9d3f55: Switch minimum supported Node version to 14 for all packages
fix(postcss-merge-rules): do not merge conflicting flex and border properties
fix: update autoprefixer and browserslist
chore: update TypeScript and improve types
fix: correct package.json dependency version specifier
feature: add TypeScript type declarations
Publish untranspiled CommonJS source
refactor: remove implicit boolean conversion
refactor: remove getMatch function from cssnano-utils
refactor: remove getMatch function from cssnano-utils
The getMatch function allows nested arrays to emulate a map. It is better to replace this function with a regular Map(). It's unlikely this function is used outside of cssnano as it requires a very specific nested array struture.
Updated dependencies
Nothing published for this version
Check all intersections when merging rules
postcss-merge-rules: add some missing known pseudo classes.
Note: Version bump only for package postcss-merge-rules
Note: Version bump only for package postcss-merge-rules
Note: Version bump only for package postcss-merge-rules
postcss version is 8.2.1Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
postcss-merge-longhand incorrect minification of border (border-*) declarations.
postcss-merge-longhand incorrect minification of border (border-*) declarations.postcss-merge-longhand don't explode declarations with custom properties.
postcss-merge-longhand don't explode declarations with custom properties.postcss-colormin now better transform to hsl.browserslist version incompatibility with caniuse-api
browserslist version incompatibility with caniuse-apiWe dropped support for Node 4, now requiring at least Node 6.9.
border-spacing property.U to lowercase for IE <= 11 and Edge <= 15.a { border-style:dotted; border-style:var(--variable) }) correctly.border property with invalid border width value correctly.:-ms-input-placeholder and ::-ms-input-placeholder selectors correctly.all property correctly.url function.data and *-extension:// URLs correctly.all for IE.Includes the new release candidate for postcss-selector-parser 3.
@media \0screen\,screen\9 {} hacks
(thanks to @evilebottnawi).resolveConfig for a Root node without a source property
(thanks to @darthmaim).Since version 4 has been in-development for some time, we thought it would be best to release an alpha version so that we could catch any issues befor
Since version 4 has been in-development for some time, we thought it would be best to release an alpha version so that we could catch any issues before the actual release.
cssnano & its plugins have been upgraded to PostCSS 6.x. Please ensure that for optimal results that you use cssnano with a PostCSS 6 compatible runner & that any other plugins are also using PostCSS 6.
cssnano is now essentially a preset loader and does not contain any built-in
transforms (instead, it delegates to cssnano-preset-default by default).
Due to the new architecture, it's not possible to exclude asynchronous
transforms and run it synchronously, unlike in 3.x. Any transforms that
were "core" modules have now been extracted out into separate packages.
Because of the new preset system, cssnano will not accept any transformation options; these must be set in the preset. The option names remain mostly the same, except some cases where "core" modules have been extracted out:
core is now normalizeWhitespace.reduceBackgroundRepeat is now normalizeRepeatStyle.reduceDisplayValues is now normalizeDisplayValues.reducePositions is now normalizePositions.reduceTimingFunctions is now normalizeTimingFunctions.styleCache is now rawCache.When excluding transforms, we now have an exclude option (in 3.x this was
named disable). Similarly, the safe option was removed; the defaults
are now much less aggressive.
By default, the following transforms are no longer applied to any input CSS. You may see an increased output file size as a result:
autoprefixerpostcss-discard-unusedpostcss-merge-identspostcss-reduce-identspostcss-zindexNote that you can load cssnano-preset-advanced instead which does contain
these transforms.
We no longer detect previous plugins to silently exclude our own, and now
consider this to be an anti-pattern. So postcss-filter-plugins was removed.
We also changed some options to make the default transforms safer:
postcss-minify-font-values: removeAfterKeyword set to false from true.postcss-normalize-url: stripWWW set to false from true.cssnano now does not accept the sourcemap shortcut option; please refer
to the PostCSS documentation on sourcemaps. The quickstart.js file included
with this module will give you a good starting point.
cssnano.process is no longer a custom method; we use the built-in process
method exposed on each PostCSS plugin. The new signature is
cssnano.process(css, postcssOpts, cssnanoOpts), in 3.x it was
cssnano.process(css, cssnanoOpts).
We dropped support for Node 0.12, now requiring at least Node 4.
Finally, cssnano is now developed as a monorepo, due to the fact that some
transforms have a lot of grey area/overlap. Due to this, some modules have
been refactored to delegate responsibility to others, such that duplication
of functionality is minimized. For instance, postcss-colormin will no
longer compress whitespace or compress numbers, as those are handled by
postcss-normalize-whitespace & postcss-convert-values respectively.
clone behaviour. In cases where some utility
has been used by several plugins it is now a separate package, reducing
cssnano's footprint.postcss-colormin &
postcss-reduce-initial were enhanced with different behaviour depending
on which browsers are passed. And now, the footprint for the caniuse-db
dependency is much smaller thanks to caniuse-lite - 7 times smaller as
of this writing. This makes cssnano much faster to download from npm!Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →