Another way to compress CSS files

image


In the image above, many will see the famous picture. This is how most CSS files on production look like. We all try to get our web pages to load quickly; To achieve this, we use various tools and techniques for optimizing page loading and rendering. About one, but rarely used method, I would like to talk and talk about how I managed to reduce the size of the CSS mail.ru mail file by 180Kb.

It would seem that the file shown above has nothing to optimize, all the spaces and line breaks are deleted, the file was processed by some kind of smart optimizer like CSSO with a structural change, and it seems that only the browser or the drunk plumber will understand such a file. But, as we know, there is no limit to the perversion of perfection, and especially optimization.

Next, we will try to squeeze all the juices from this file.

If you look closely, we will notice something interesting in this file.

image

Please pay attention to class names. You probably already guessed what the discussion was about - about reducing the length of these names. Because with the advent of BEM and other CSS writing techniques, class names have become quite long, and sometimes indecently long.

Life examples


I conducted an analysis of CSS files, some popular resources and confirmed my assumption:

image
The image shows the classes that are found in production versions of CSS files of these sites.

Probably, no one needs to explain the significance of the speed of the initial page loading and its effect on user satisfaction, conversion, website traffic. With the advent of mobile devices and mobile Internet, this problem is very acute. For example, downloading 40Kb on 4G Internet will take an average of 700ms, and if you consider 3G, EDGE, GPRS, WiFi at rush hour in a cafe or metro, the download time will increase significantly. Every kilobyte saved is worth its weight in gold.

If we fight for every space and line break in CSS files, mercilessly cutting them, then why not compress the names of classes and identifiers to two or three characters? After all, it’s absolutely not important for your users what the block class of the page will be called, but the page loading speed will be very important for them. Perhaps this is why the guys from Google use this technique (on google.com):

image

From words to deeds


So, with that, why this is needed, I hope, figured out. Let's get down to business. The first problem is that we cannot just take and reduce classes and identifiers in CSS files, because they are used in typesetting, templates, and JS files. Replacing only in one place - everything will break. It becomes obvious that you need to replace the names in the whole bunch of files. In my projects for minification, gluing and other such tasks I use Grunt.js. This build system is one of the most popular front-end development worlds. Catching up with a quick search for plugins that implement this task, I did not find anything and decided to write my bike plugin.

grunt-revizor


Thus was born the grunt-revizor . When writing it, I ran into several problems. Everything is simple in CSS files, the syntax is known in advance, .name or #name, but in other files it may look different. For example, in HTML
, i.e. name already without a dot or in JS files
document.getElementById ('name')
also without #, which complicates the problem of finding and replacing names. What is most scary is that you can break something, for example, if the CSS class name is ".success" and we have the "success" variable in the JS file, replacing it we break the JS code. And we can’t allow this, so we had to introduce requirements for spelling names, namely, the name must end with a unique prefix, which will definitely make it possible to distinguish the class from something else JS, HTML and other files. For example, .b-tabs__title-block--, in this case the prefix '-'.

Task example:

grunt.initConfig({
  revizor: {
    options: {
        namePrefix: '__',
        compressFilePrefix: '-min'
    },
    // Найдет файлы: test/css/style1.css, test/css/style2.css, 'test/js/main.js и др...
    src: ['test/css/*.css', 'test/html/*.html', 'test/js/*.js'], 
    dest: 'build/'
  },
});

After starting this task, grunt will find all CSS, HTML and JS files that correspond to the paths from src, find in CSS files all class names and identifiers ending with the __ prefix, compress the found names to two and three character, for example .b-tabs- -notselected__ -> .zS, after which it will save new files in which all found matches will be replaced, and save them in the build folder, with the names style1-min.css, index-min.html, main-min.js.

CSS example:

/* Original: style1.css */
.b-tabs__title-block-- {
  color: red;
  font-size: 16px;
}
.b-tabs__selected-- {
  font-weight: bold;
}
/* ======================================= */
/* Result: style1-min.css */
.eD {
  color: red;
  font-size: 16px;
}
.rt {
  font-weight: bold;
}

JS example:

/* Original: main.js */
var $tabmenu = $('#tabmenu--');
var tabmenu = document.getElementById('tabmenu--');
if ($tabmenu.hasClass('b-tabs__selected--')) {
  $tabmenu.removeClass('b-tabs__title-block--');
};
/* ======================================= */
/* Result: main-min.js */
var $tabmenu = $('#j3');
var tabmenu = document.getElementById('j3');
if ($tabmenu.hasClass('rt')) {
  $tabmenu.removeClass('eD');
};

The same principle applies to HTML, template, and other files.

Some features of the plugin:
  • First 2-character names are generated. After the number of names exceeds 2500, 3-character names will begin to be generated;
  • To generate names, lowercase and uppercase letters of the Latin alphabet are used, numbers, symbols - and _ ;
  • There is a check for collision, i.e. a compressed name can only match one full name.


Optimization by examples


Let's try to find out the potential benefits of such optimization using the same popular resources as an example. I’ll tell you how I will count. Of course, I will not pass CSS files through grunt-revizor, because they do not use a unique prefix. Using a simple Node file, I will count the total number of classes, calculate their length in bytes, and calculate the potential file size from this. The simplified formula looks like this:

names = ['b-block1','b-block2','b-block3', ....];
saveSize = allNamesSize - (names.length*3)
// allNamesSize - Общая длина всех символов всех найденных классов
// 3 - Итоговое количество символов в сжатом название


WebsiteOriginal File SizeCompressedIn percents
Mail.ru mail (Desktop version)849 Kb182.5 Kb26.4%
hh.ru (Desktop version) two files109.8 Kb30 Kb27.3%
vk.com (Mobile version) two files184.1 Kb48.5 Kb26.4%
Yandex.Taxi (Mobile version)127 kb13 Kb10.3%
2gis.ru (Mobile version)1293 Kb172 Kb13.3%

Normal files were used, not gzip. The optimization data is approximate, because in addition to CSS files, styles were sometimes in the html page, respectively, were not taken into account here. We used a simple regularization for parsing, which looked only for classes, though not all the possible ones, and it did not look for identifiers. In addition to CSS compression, there will be a gain on other files, html, templates, etc., which are also not taken into account here. I note right away that some files do not serve such optimization very well, for example, files where there are few classes or short names are used, or a lot of images inserted through data: URL, which is shown on 2gis and Yandex.Taxi files.

Conclusion


Of course, I did not come up with anything new. This method is familiar to many, but it is rarely used, because it has both cons and pros. For example, it introduces difficulties in development, clean code, etc. By and large, gzip partially solves the problem of long names and compresses files great, but nevertheless, a certain gain in the size and time of decompressing files can be obtained using gzip. For example, the optimized compressed mail.ru file is 20Kb smaller than the same unoptimized compressed file.

I also want to say that my plugin is not a silver bullet in this matter, but only an example of implementation. Perhaps in the future, the plugin will get something really worthwhile. I will work closely on this idea, ideally I would like the plugin to work with absolutely unprepared files, that is, without using a prefix, but do everything automatically and accurately.

The point of my entire article is to raise the interest of developers in this matter, to listen to the opinions of others. Maybe someone already had a negative or positive experience in this matter. Please share it. In addition, the same Google, I think, is not in vain using this method.

In general, there is something to talk about and something to think about.

UDP: Soon I will answer all questions, give specific files, and compare the gain when using GZIP.

Also popular now: