Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatically Combining / Merging CSS Selectors with Preprocessors

A basic CSS example. Every browser I have come across will render the item with the margin & padding and a red border

.test{
    margin: 4px;
    border: 1px solid black;
    padding: 4px;
}

.test{
    border: 1px solid red;
}

Naturally if I was writing this CSS by hand I would replace the black with red and only have one rule.

But If the first rule comes from a parent CSS file (or in my case a LESS file) that I can't edit because it is used elsewhere, or is from a 3rd party library that I don't want to hack then I see no alternative but to add an extra rule.

Now since I am using server side LESS -> CSS compilation with minification, it seems perfectly reasonable to me that the compressor/minifier should reduce the rules down to just

.test{
    margin: 4px;
    border: 1px solid red;
    padding: 4px;
}

But everything I have tried, keeps both rules; some of the compressor/minifiers go as far as removing newlines

.test{margin:4px;border:1px solid black;padding:4px}.test{border:1px solid red}

It strips out a single newline character but left an entirely unnecessary rule declaration in. This seems bizarre to me.

Is there any system than can do this? (preferably an add on for node.js) If not, do we know why not? Seems like quite a big file size saving with no downside to me.

disclaimer I have tried searching for combining selectors, merging selectors and few variations, apologies if I have missed the common term for this procedure, seems likely since the gains just seem so obvious I have to be missing something!

like image 955
CodeMonkey Avatar asked May 24 '13 09:05

CodeMonkey


People also ask

How can we combine two selectors in HTML?

The descendant combinator — typically represented by a single space (" ") character — combines two selectors such that elements matched by the second selector are selected if they have an ancestor (parent, parent's parent, parent's parent's parent, etc) element matching the first selector.


1 Answers

Why It Cannot and Should Not Be Done

You state:

Every browser I have come across will render the item with the margin & padding and a red border.

That is because, of course, the cascading nature of CSS. It is designed to work like that for the express purpose of overriding. Which is exactly the point of why (and how) you "add an extra rule" in your CSS to override.

There is a Reason

Now, I can see your point in a preprocessor perhaps "merging" code with the same selector for minimization purposes. However, (1) there would be a rare (if ever) case where the two classes would actually follow one right after the other in the CSS code, as your example shows (and in such a case, minimization would be okay). Usually there is going to be intervening CSS that can affect how the cascade might play out in rendering. Which leads to (2), it would require more logic than is initially obvious (or even possible) to implement. Consider this example:

HTML

<div class="test1 test2"></div>

CSS (Framework File)

.test1 {
    margin: 4px;
    border: 1px solid black;
    padding: 4px;
}

.test2 {
    border: 1px solid blue;
}

CSS (Developer File)

.test1 {
    border: 1px solid red;
}

The above code if output as normal should render a red border by the cascade, just as the developer wants. Now suppose LESS or another preprocessor does minify it as you desire. It could end up like this:

Theoretical Minimization

.test1 {
    margin: 4px;
    border: 1px solid red;
    padding: 4px;
}

.test2 {
    border: 1px solid blue;
}

And would in fact render as blue not as red! This is because the two .test1 merged, now making the .test2 last in the cascade order rather than the second instance of .test1 being last. So a preprocessor would have to be "smart" enough to figure out a theoretically infinite number of possible cascade combinations, and that without knowing what the html coding is that ultimately influences the decision (like here, where the html double classes in conjunction with the cascade order is what is determining the final rendering).

Had the preprocessor merged into the second instance, that does not solve the problem, as what if developer had put a second instance of .test2 after the second instance of .test1, but did not define a different border color? The .test2 border color would still have overridden by merging with the following .test2.

This illustration should show why such a minimization cannot and should not be done--the interactive logic between possible html form and CSS cascade is impossible to predict what or how to merge except in a case were two exact selector strings in the CSS immediately followed one another. Any other case could make a wrong decision.

like image 107
ScottS Avatar answered Sep 30 '22 16:09

ScottS