mrclay/minify

_selectorsCB callback strips off whitespace inside calc() expressions

sedimentation-fault opened this issue · 0 comments

Problem

Take this CSS as an example:

body {
	font-size: calc(15px + 0.39vw);
}

body, #contents {
	width: 100%;
}

minify will transorm it to

body{font-size:calc(15px+0.39vw)}body, #contents{width:100%}

which is incorrect, because '+' and '-' inside calc() expressions must retain whitespace around them:

The + and - operators must be surrounded by whitespace. For instance, calc(50% -8px) will be parsed as "a percentage followed by a negative length" — which is an invalid expression — while calc(50% - 8px) is "a percentage followed by a subtraction operator and a length". Likewise, calc(8px + -50%) is treated as "a length followed by an addition operator and a negative percentage".

(from: MDN - calc())

Reason

The _selectorsCB() callback in minify/lib/Minify/CSS/Compressor.php is passed
' body{font-size:calc(15px + 0.39vw)}body, #contents{'
(the string inside the single quotes, without the quotes, but including the blank at the start) as parameter and interprets it as a "selector" for 'body', so it strips whitespace out of it.

Solution

Replace

    protected function _selectorsCB($m)
    {
        // remove ws around the combinators
        return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
    }

with

    protected function _selectorsCB($m)
    {		
        $pos = strpos($m[0], 'calc(');
	if ($pos !== false) {
		return $m[0];
	}

        // remove ws around the combinators
        return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
    }

i.e. if the "selector" contains 'calc(' don't do anything with it. Maybe not a true solution [*], but at least a workaround...

[*] because, theoretically, you would want to leave only the calc() part of $m[0] untouched, not the whole $m[0], but anyway...