I use LessCss as CSS preprocessor in my every day projects and I really like the benefits I gain from a developing time perspective. I can't work without it anymore!
Unfortunately CSS preprocessors are not known to generate optimised CSS code!
There are a lot of discussions around this argument and the most often given answer is something like:
- Preprocessors are not optimization tools
- If you write bad LessCss then you obtain bad CSS
I could not agree more!
This article is about to point out the problem and to present a possible LessCss best practice in order to generate optimised CSS code out of the box.
The following LessCss source code represent a classic multi-file approach where different responsabilities are spreaded into many source files.
{% highlight css linenos %}
// structure.less
.btn {
display:block;
border: 2px solid black;
}
// colors.less
.btn {
background: #ddd;
color: #444;
text-shadow: 0 1px 0 #fff;
}
// font.less
.btn {
font-family: Tahoma;
font-size: 14pt;
}
{% endhighlight %}
The generated code is far to be good!
{% highlight css linenos %}
.btn {
display: block;
border: 2px solid black;
}
.btn {
background: #ddd;
color: #444;
text-shadow: 0 1px 0 #fff;
}
.btn {
font-family: Tahoma;
font-size: 14pt;
}
{% endhighlight %}
The same selector
.btn
is repeated over and over again causing the browser to resolve the xpath many times.
Code above is really slow to render (ok, it's slow from a CSS rendering perspective!), but you can figure out the consequenques of this approach extended to a real project stylesheet!
I wish to compact all these instructions into a single selector like the following:
{% highlight css linenos %}
.btn {
display: block;
border: 2px solid black;
background: #ddd;
color: #444;
text-shadow: 0 1px 0 #fff;
font-family: Tahoma;
font-size: 14pt;
}
{% endhighlight %}
The way I propose to solve this problem combines two preprocessor concepts:
The goal is to write a non outputting mixin, extend it serveral times and apply it to a single selector only when every other manipulations are over.
{% highlight css linenos %}
// classes.less
.classBtn() {}
// structure.less
.classBtn() {
&:extend(.classBtn);
display:block;
border: 2px solid black;
}
// colors.less
.classBtn() {
&:extend(.classBtn);
background: #ddd;
color: #444;
text-shadow: 0 1px 0 #fff;
}
// fontx.less
.classBtn() {
&:extend(.classBtn);
font-family: Tahoma;
font-size: 14pt;
}
// selectors.less
.btn {
.classBtn;
}
{% endhighlight %}
The generated code looks exactly how I wished:
{% highlight css linenos %}
.btn {
display: block;
border: 2px solid black;
background: #ddd;
color: #444;
text-shadow: 0 1px 0 #fff;
font-family: Tahoma;
font-size: 14pt;
}
{% endhighlight %}
- Unoptimised - standard Less code
- Optimised - exploit Less features to produce nice CSS code!
When it comes to CSS code optimisation all the major CSS preprocessors sucks a lot.
None of them are able to run the minimal amount of optimisation like removing overridden properties or packing many instances of the same selector.
I think this is the right behavior of such kind of tools!
They are preprocessor, not optimisation tools!
Nevertheless preprocessors give the developer a lot of powers over the basic CSS standard: you can nest selectors, you can use variables, mixins...
With great powers comes great responsibility
Francois-Marie Arouet (Voltaire)
When you decide to use a preprocessor then you start learning it's rules and features.
Very seldom you study about the risk in using them!
Well, the risk in using (or abusing) preprocessors are in the generated CSS performances, or in the defect of performances!
- code repetition
- deep selectors
- verbose selectors
All of these are possible causes of bad CSS performances and must be well understood and considered when it comes to write CSS for a complex page or WebApp!
You can see what I'm talking about in these simple Codepen examples.
I explicitly wrote some code repetition and I can observe that nothing is done by the preprocessor to optimise my code: