Talk is about CSS minification problem, how minifiers work, new advanced optimisations, how minification influences on performance, CSSO reborn and future plans.
22. 13
clean-css 3.4.9 cssnano 3.5.2 csso 2.0.0
ActiAgent.ru
602 233 bytes (5 month ago)
430 240
1 077 ms
439 024
23 270 ms
435 588
531 ms
ActiAgent.ru
822 021 bytes (now)
587 906
1 705 ms
604 503
48 550 ms
595 834
616 ms
gzip compression factor is 8 (~72Kb)
That means the result can be improved!
Our numbers
31. What to delete
• Whitespaces and comments (most part of saving)
• Rules with wrong selectors
• Empty rules
• Wrong declarations
• Malposed @import, @charset
• …
22
36. But it's not so simple
• 0px ! 0
correct
• 0deg ! 0
wrong, not a length unit
26
37. But it's not so simple
• 0px ! 0
correct
• 0deg ! 0
wrong, not a length unit
• flex: 1 0 0px ! flex: 1 0 0
wrong, doesn't work as expected in IE
26
41. More examples
• Number normalization: 0.00 ! 0 or 0.123 ! .123
• Special values for some properties
• font-weight:bold ! font-weight:700
• background:none ! background:0 0
• from ! 0%, 100% ! to for @keyframes
• …
30
52. 39
span {
color: red;
}
div {
color: green;
}
ul {
color: red;
}
span, ul {
color: red;
}
div {
color: green;
}
Regrouping
Correct?
53. 39
span {
color: red;
}
div {
color: green;
}
ul {
color: red;
}
span, ul {
color: red;
}
div {
color: green;
}
Correct,
elements have a single name
Regrouping
Correct?
60. 44
.foo {
color: red;
}
.bar {
color: red;
color: rgba(..);
}
.foo, .bar {
color: red;
}
.bar {
color: rgba(..);
}
Moving common parts aside
In this case only moving up is correct
61. 45
.foo {
color: rgba(..);
}
.bar {
color: red;
color: rgba(..);
}
.bar {
color: red;
}
.foo, .bar {
color: rgba(..);
}
Moving common parts aside
In this case only moving down is correct
63. Basic optimization summary
• Common approaches
• Usually whitespace deletion is most effective
• Many hacks and edge cases
• Every minifier has bugs
47
69. 53
{
"classes": ["foo", "bar"],
"tags": ["ul", "li"]
}
.foo { color: red }
div.bar { color: green }
ul li, ol li { color: blue }
usage.json
CSS
+ .foo { color: red }
ul li { color: blue }
Result
71. 55
.module1-foo { background: red; }
.module1-bar { font-size: 1.5em; background: yellow; }
.module2-baz { background: red; }
.module2-qux { font-size: 1.5em; background: yellow; width: 50px; }
Rules with .module1-foo and .module2-baz
can't be merged,
since .module1-bar is between them
72. 56
.module1-foo { background: red; }
.module1-bar { font-size: 1.5em; background: yellow; }
.module2-baz { background: red; }
.module2-qux { font-size: 1.5em; background: yellow; width: 50px; }
Rules merge is safe only if we sure that class
names are never applied to a single element
73. Usage data
57
{
"scopes": [
["module1-foo", "module1-bar"],
["module2-baz", "module2-qux"]
]
}
With this JSON we explain to optimizer that
module1-* and module2-* class names
are never applied to a single element
74. The basic minification result (157 bytes)
58
.module1-foo,.module2-baz{background:red}
.module1-bar,.module2-qux{font-size:1.5em;background:#ff0}
.module2-qux{width:50px}
34 bytes extra saving
.module1-foo{background:red}.module1-bar{font-size:
1.5em;background:#ff0}
.module2-baz{background:red}.module2-qux{font-size:
1.5em;background:#ff0;width:50px}
The result with usage data (123 bytes)
79. 63
.foo { color: red }
.foo.bar { color: green }
.a { color: red }
.a.b { color: green }
{
"foo": "a",
"bar": "b"
}
Rename map
Result
CSS
+
80. 64
• 823 Kb Orignal CSS
• 596 Kb Basic optimization
• 385 Kb Rename (currently outside CSSO)
211 Kb of extra saving (35%)
Profit for our project
81. 65
364Kb of extra saving (61%)
All together
• 823 Kb Original CSS
• 596 Kb Basic optimization
• 232 Kb Rename + Usage data
82. Should it be a part of minifier?
66
Yep, because it leads
to a new optimization
83. 67
.foo,
.bar {
color: red;
}
.foo:hover,
.bar:hover {
color: green
}
.a { color: red }
.a:hover { color: green }
{
"foo": "a",
"bar": "a"
}
Rename map
ResultCSS
+
The single new name for old two
names – reduce selector count
87. 71
• ~10 Kb of extra saving (~3-4%)
• 1431 of 6904 selectors were deleted
Selector count decreased by ~20%
Profit for our project
quick and dirty numbers
95. Original CSS 823 Kb – 35ms
Basic optimisations 596 Kb – 29ms
Rename 385 Kb – 24ms
Rename + usage data 232 Kb – 22ms
79
Quick and dirty tests
Various level of compression and its influence on Parse Stylesheet
Size is reduced by ~4 times, time by ~40%
(Chrome on MacBook Air)
96. Win10 Desktop 19ms → 11ms
Nexus 5X 68ms → 44ms
Samsung Galaxy Note 2 158ms → 108ms
80
Quick and dirty tests
On other devices there were more encouraging numbers
CSS 316Kb 215Kb (-39.5%)
+ usage data
101. What was changed
• 10+ times faster
• 8+ times less memory consumption
• Fixed most of bugs
• Better code base and API
• More downloads and stars on GitHub ;)
84
102. 85
1 300 000+ downloads per month
9x since October 2015
103. Compressiontime(600KbCSSfile)
500 ms
1 000 ms
1 500 ms
2 000 ms
2 500 ms
3 000 ms
3 500 ms
4 000 ms
4 500 ms
5 000 ms
5 500 ms
6 000 ms
CSSO version
1.4.0 1.5.0 1.6.0 1.7.0 1.8.0 2.0
1 050 ms
clean-css
Performance changes
csso
500 ms
cssnano
23 250 ms
104. postcss-csso
87
Plugin for PostCSS
As fast as CSSO alone
Under the hood the plugin converts PostCSS AST into CSSO
format, optimises it and converts back
github.com/lahmatiy/postcss-csso
105. New features
• Source Maps
• Usage data
• Support for new parts of CSS
• User friendly error messages
• Support for stdin
• New AST format
88
108. Coming soon
• New algorithms and optimizations
• Browser support modes
• Property families and declaration sorting
• Name normalization and renaming
• Shorthand properties structure recognition
• And more…
91