From 115e4fee30b0d696a9991b3ee906c5dabfcdb68d Mon Sep 17 00:00:00 2001 From: Shinsuke Sugaya Date: Sat, 5 Sep 2026 09:14:44 +0900 Subject: [PATCH] Stop zero-value minification from producing invalid CSS Two long-standing rules in CssCompressor rewrite a zero into a form the CSS grammar does not accept in that position. A browser drops the whole declaration, so the rule costs the declaration to save a byte, and it does so silently - the build reports no warning. Zero-percentage stripping inside a group hsla(0,0%,100%,.5) -> hsla(0,0,100%,.5) rgb(0%,50%,100%) -> rgb(0,50%,100%) color-mix(in srgb,red 0%,b) -> color-mix(in srgb,red 0,b) min(0%,10px) -> min(0,10px) The rule "a zero may drop its unit" is true, but it was applied to as well, and it fired on anything inside a "(". The colour functions' comma-separated legacy form takes arguments, and the math functions type-check their arguments against each other, so none of the outputs above is a valid value. The "%" case now consults the enclosing function name. The name is captured rather than looked behind, so "min" does not also match the tail of "minmax()", where a zero really may lose its unit. Length units inside groups, gradient colour stops and minmax() are unchanged. Collapsing a run of zeroes box-shadow:0 0 -> box-shadow:0 text-shadow:0 0 0 -> text-shadow:0 perspective-origin:0 0 -> perspective-origin:0 "margin:0 0 0 0" is the box-model shorthand and collapses exactly, but a needs both of its offsets, so "box-shadow:0" is invalid - and taking the declaration down takes every other shadow in the same comma-separated list with it. "perspective-origin:0" means "0 center", not "0 0". The collapse now matches on the property name (vendor prefix removed) and skips those three; "flex" keeps the exclusion it already had. Verification - 143 real-world stylesheets (Bootstrap, Font Awesome, Swiper, Vuetify and Fess/CodeLibs themes) were compressed and every declaration checked against the CSS grammar with css-tree's lexer. Declarations valid in the source but invalid after compression: 26 files before, 0 after - the 16 files still flagged are var() fallbacks css-tree cannot resolve, and are flagged identically in the source. - All 143 stay byte-identical when compressed a second time. - CssColorFunctionTest pins each corrupted value plus the neighbouring cases that must keep compressing: minmax(), gradient stops, translate(0px), margin/padding/border-radius/gap, background-position, transform-origin. - The three percentage entries and the min() entry in KnownCssLimitationsTest recorded these defects with instructions to delete them once fixed; they are removed. The