
CSS Reminification: A crazy idea that worked - remy_luisant
https://luisant.ca/remynifier
======
bastawhiz
Author of crass here.

I wouldn't recommend doing what the author suggests. crass doesn't squeak out
extra bytes when you reprocess because it already does this for you.

If you combine multiple minifiers, the bugs in one minifier can end up being
amplified by the others. For instance, one minifier might not consider
!important when moving around CSS. Another minifier might then take that
output and perform an optimization that's no longer just unsafe, but now
incorrect. It might even delete rulesets that it believes are not used,
ruining your stylesheet.

There are suites that test the correctness of minifieres, and many don't do
great. CSS still "works" when the syntax is invalid, so invalid stylesheets
result in undefined results when you minify. Between bugs and undefined
behavior, I wouldn't recommend mixing and matching just to save one or two TCP
packets, especially with gzip/brotli on the wire.

~~~
remy_luisant
I'm not recommending doing it either! :)

~~~
wereHamster
I don't recommend using minifiers which contain bugs or do unsafe
transformations (a form of bug IMO).

~~~
bastawhiz
> I don't recommend using minifiers which contain bugs

I recommend not using minifiers. Or compilers, for that matter. Or really any
software. ;)

> or do unsafe transformations (a form of bug IMO)

I think it depends. In crass, unsafe transformations are only performed when
you pass a special flag to the API or CLI. The constraints and effects are
documented with the premise "If you're not doing X, Y, and Z, we'll squeeze
out a few more bytes". X, Y, and Z are things like relying on browser bugs or
only using deprecated features (e.g., using -webkit-border-radius without
border-radius). Folks using CSS preprocessors (SCSS, stylus, LESS, etc.)
oftentimes don't have CSS that could cause issues, and this option is safe
enough for them (and provides a hefty benefit).

The problem with CSS is that you can guarantee many things as safe, but
because of the flexibility of the language, it's impossible to minify
"perfectly". Having knobs and levers for the developer to say "I'm not
supporting IE9 or below, yes you're looking at ALL of my CSS, no I don't care
about browsers older than three years..." gives much better results--
especially on large projects--by trimming down the number of variables that
could prevent different sorts of "optimizations".

------
kzrdude
Chaining was not exactly the level of innovation that the introduction set up
for.

~~~
remy_luisant
I'm an academic. I get very unwell if I'm not pompous at least half of the
time. I also had two days to put this together, from conception to submission
here. I could have been more innovative, but given the time limit...

The best part was that the whole description is technically correct.

~~~
ars
You should apply this to png optimization. A combination of: optipng, pngout,
pngcrush, DeflOpt, advpng.

It's a bit more complicated since you have to also vary the command line
arguments you pass to the program. (Different color types can sometimes help,
as can different filters.)

~~~
masklinn
That's a pretty old tech. So old that many existing software package do that
for you e.g. ImageOptim for OSX will run all of Zopfli, PNGOUT, OptiPNG,
AdvPNG and PNGCrush using a nice drag-and-drop interface.

~~~
nailer
It does SVG now too! (Edit: heart, why doesn't HN support emoji?) ImageOptim

~~~
tambourine_man
Because we are grumpy old grey beards. And it wouldn't fit with the background
color, of course.

Can you imagine scrolling throught all that promiscuity of color and shape?
Shivers. ASCII4ever!

~~~
masklinn
> Because we are grumpy old grey beards.

Or it's because HN's commenting system is a trash fire and whoever coded it
decided they couldn't be arsed to implement escapes, quotes, lists or fucking
tables but _just had_ to strip out arbitrary symbols and obviously U+2495
NUMBER FOURTEEN FULL STOP (⒕) is important but god forbid anybody use U+262C
"ADI SHAKTI" (<should be here but HN strips it out, see
[https://en.wikipedia.org/wiki/Khanda_(Sikh_symbol)](https://en.wikipedia.org/wiki/Khanda_\(Sikh_symbol\))
>) or even better U+2299 "CIRCLED DOT OPERATOR" (⊙) makes the cut but U+2609
"SUN" (<also a dot inside a circle just a different one>) shall be banned from
using or discussing.

~~~
andai
Wait, what? How did you find that out?

~~~
masklinn
I knew HN stripped symbols pretty arbitrarily, I just checked a few non-letter
Unicode blocks (Enclosed Alphanumerics[0], Mathematical Operators[1], Misc
Symbols[2]) and checked which one HN strips.

[0]
[https://en.wikipedia.org/wiki/Enclosed_Alphanumerics](https://en.wikipedia.org/wiki/Enclosed_Alphanumerics)

[1]
[https://en.wikipedia.org/wiki/Mathematical_Operators](https://en.wikipedia.org/wiki/Mathematical_Operators)

[2]
[https://en.wikipedia.org/wiki/Miscellaneous_Symbols](https://en.wikipedia.org/wiki/Miscellaneous_Symbols)

------
BuuQu9hu
I worry that this is the chaos of folks not understanding compiler theory.
This is the result: every minifier is a compiler, but none of these minifiers
boast properties like idempotence and none of them are sufficiently correct
that they achieve their goal on the first try. I don't expect to necessarily
see scholarly papers, but I would not ever expect a minifier to improve on
itself when run multiple times in a row!

~~~
nine_k
This is one of the problems with IT: it has too little science behind it. A
lot of things are built based on common sense, tradition, and chutzpah. Much
like building was in medieval times. No wonder things often crash and take a
hard-to-predict time to build.

Some things can be over-engineered but under-scienced, so to say, at the same
time.

A lot of great computer science already exists, and is applicable, and some of
it is taught at universities. But it's not required for a CRUD app or for a
flappy bird clone. And when you grow up enough to want more complex things, a
lot is already forgotten.

~~~
sidlls
I have, I think, a different view: there is too much CS and not enough
engineering in IT. The numerous CS grads have a bag of hammers as their
toolset and so every engineering problem is a DS&A "nail." At least, here in
the valley it seems that way. Outside of it there is at least in my experience
much more of a nod toward software development as an engineering endeavor
rather than as an exercise in pure CS.

~~~
nine_k
Yes, the theoretic / scientific part does not percolate enough into the
engineering practice. Thus the practice is starving without a good conceptual
view, while CS may be lacking in engineering department and thus practicality.

------
Exuma
Your site is very nicely styled and easy to read... I like how large and
legible and clean-looking the font is.

Also, your writing style is funny, nice work.

~~~
nimchimpsky
ha, I thought the site was horrible. Too big.

~~~
astrodust
HN: For all your constructive criticism needs.

------
sbierwagen
If you really want to get fancy you could use Selenium to get screenshots and
compare them to check that the remynified CSS produces the same layout as the
original CSS.

[http://www.seleniumhq.org/](http://www.seleniumhq.org/)

~~~
Khalos
This would be great, but would probably significantly hurt performance.

Especially if media queries come into play and we'd have to try different
resolutions and ensure they're all the same.

~~~
askmike
> Especially if media queries come into play

Or even more simply: if your CSS is supposed to work on all pages of a
website.

------
huula
Transforming .a-really-long-class-name-or-id into something shorter, like .x
would save a lot more bytes.

Another thing is splicing unecessary properties, which can even save more
bytes, but given web apps becoming becoming so dynamic right now, that would
be hard or really awkward for developer to work with too.

~~~
gkoberger
This isn't something a minifier could do without also modifying HTML and JS,
and since a lot of JS automatically generates CSS names (something like
$(`.list-${i}`)), this would be nearly impossible.

The programmer could shorten it when writing, but is the extra half a
milisecond worth having to work with x, y and z as classes rather than more
explicit classes?

~~~
sly010
While I am not aware of any open source framework doing this, as long as the
programmer holds itself to a few basic rules, this should be possible. If all
html/js/css is generated by a template language, you keep the css classes
easily parsable and you avoid using dynamic classnames in js (data properties
are better for that anyway), you can just replace all classes with random ids.

In fact this is how gettext() works.

~~~
jkaptur
The Closure Compiler is open source and can do this.

~~~
sly010
Nice! I didn't know the closure compiler toolkit handles css/html templates
too. That explains why googles classnames are mangled.

------
quakenul
I really can't tell if this is satire. But either everyone is in on the joke
and out to get naive people like me (in that case, well played) or Hacker News
does not seem to think so.

I don't think there is one single use case, on any scale, anywhere in the
world, where saving up to 17% on a minimized css file before gzip would matter
in the slightest, let alone while adding 20mins to your build cycle :)

~~~
cobookman
Saving 17% on a the google.com, Facebook.com, and Amazon.com CSS files would
make a difference.

Now that also depends on what the TCP packet size is. For example saving 1Byte
might not lessen the amount of network traffic needing to be sent.

~~~
underwater
This discussion reminds me of this exploration into compressing JSON data.
Unless you're considering the final gzipped size you're doing it wrong:

"In short, if you try to eliminate redundancy by applying some kind of clever
transformation or restructuring your data object before piping it to gzip, you
will probably just do a crappy job of reimplementing gzip at the wrong layer
of abstraction, and get worse results overall." \--
[https://github.com/wincent/relay/blob/hack/contrib/lightweig...](https://github.com/wincent/relay/blob/hack/contrib/lightweight-
results/README.md)

~~~
jordanwallwork
That's a really good point. A minified that output code directly optimised to
gzip efficiently would be a great thing

------
bluetwo
I am not a fan of css minimizers.

I'm more a fan of writing compact, clean, logical css in the first place.

A couple months ago I worked on a project and re-wrote a 129k minified css
file someone created as a clean un-minified 12k css file that had 100% of the
original functionality plus some additional UI improvements.

You can only get these improvements if you understand what you are writing and
stop using sass to write bloated files.

~~~
minitech
The two things aren’t exclusive. What reason do you have not to minify your
compact, clean, logical CSS?

------
destructionator
`rm bootstrap.css` look, a 100% reduction! And it led to a better website.

`gzip goodfile.css` and there's an improvement several times more effective
than even the best minifier. And it keeps your source code legible in the
browser and doesn't require a slow/buggy asset pipeline to test changes.

Yes, yes, I know minify+gzip can save like an additional 1% over what gzip
alone does. To me, that's just not worth the cost to the developer.

~~~
xtf
And then HTTP/2 helps more, because it does not open a new connection for each
file.

~~~
destructionator
HTTP 1.1 also does not open a new connection for each file.

------
rodrigocoelho
What really reduces css filesize for me is uncss[0]. It removes the unused
styles and then I run the css through a minifier to finish the job.

[0] [https://github.com/giakki/uncss](https://github.com/giakki/uncss)

------
xiamx
Great work! There are several directions where you can make your work more
substantial. For once it would be interesting to see what's the marginal
benefit of iterative minification over iterations (can be represented in a
graph). Does Remynification approach an asymptotic size value, will further
minification actually increase the size instead of decreasing? Also, it would
be interesting to take screenshot using selenium as suggested by sbierwagen
and see if Remynification actually preserves initial layout. Lastly, it would
be interesting to see a theory proposed as why such method work, and what can
future minifier learn from this.

------
Exuma
I would be extremely curious to find out what modifications were made by the
minifiers in round 2 and onwards that it couldn't have caught the first
time...?

JS or HTML I suppose I could see, because they're more complicated, but CSS by
itself is very simple, so I'm wondering what actually got left out.

Do you have any diffs or info on what it was removing or doing?

~~~
remy_luisant
No, but you can do this yourself quite easily. I am way too tired and sleep
deprived to do more on this for at least a couple of days. Even then I have
other priorities.

------
plaguuuuuu
In practice this is going to be gzipped before being sent over the wire right?
What's the gzipped size difference?

Something something entropy

~~~
seanwilson
It'll be smaller because gzip can only losslessly compress what is there but
minifying first can lossfully remove characters like comments or with
conversions like "0px" to "0".

~~~
noway421
Not necessarily. jQuery for example decided against some uglify parameters
because their gzipped version would have been bigger.

------
quink
I found that rearranging by desired output seemed to work better, especially
once you consider that compression works best on repeated longer strings. With
minification as it stands you're forced to have the longer strings once, with
the short strings (the CSS properties) repeated a lot.

This is exactly backwards from what you want. You want the short strings to
appear infrequently, and the longer strings a lot.

CSS resets sheets are a bad example for this kind of thing, as they're
strongly sorted by desired output property, but for general CSS for something
with a lot of components, for example, or a CSS sheet with page template
specific styling, it seems like it should minify and gzip a lot better.

Plus, you can group your CSS by relevant section, i.e. keep your colours
separate from your alignment, from your fonts, etc.

Except the problem is that doing it this way requires a bit of rearranging of
the rules, which may cause some trouble in a fairly small number of cases, so
that's why it's out as an automated way of minifying things.

------
cjhanks
Does your "reminified" CSS actually compress better under GZIP than any of the
independent minifiers? The variance you were seeing looks to be irrelevant in
comparison.

~~~
remy_luisant
I really should be doing something else, but this is too interesting.

It doesn't yet, but it could. Keep it mind, at the worst case scenario I get
to pick which of the four individual minifiers I run. I will always at least
be able to match the best one.

There is absolutely nothing that prevents me from running zopfli over the
final outputs and comparing the sizes. I can use the compressed size to drive
search. A matter of a change in metric used to consider what is an
improvement.

I'll add this to my todo list on this project. Looks like I may be doing more
on this than I originally intended...

~~~
barrystaes
And while at that, load it PhantomJS and measure parse and render time. Then
try to figure out what weight to give bandwidth VS cpu performance, realize
other things can hog the cpu, get an icecream and give up.

------
Brockenstein
"It took 25 extra minutes to save this 261 bytes. Worth it? You decide."

I think I've decided it's obviously not worth it, even if we built a time
machine and sent the css back to 1955 when they'd appreciate 261 bytes
difference. (Granted they had no use for CSS in 1955).

------
dahart
I totally read the title as "Remnification" and "Remnyfication". I didn't see
the right spelling until I got to section 5! "OH! RE-minification"... ;)

Very nice, this is a fun project and a nice write up. I would definitely worry
about lossy minification on production code, I've bumped into many minifier
bugs that broke my valid CSS.

Also, pretty sure you could get Bootstrap.css down to a couple k-bytes and
really truly pwn the file size leaderboard if you could dead-strip all the
rules not actually referenced in your HTML & JS.

~~~
Falkon1313
This is a good point. On any sizable site, especially one that has evolved
over time, you can't really remove CSS, it just gradually accretes more and
more. But most of the rules aren't even used on most pages. Being able to
first remove anything not used, then remove anything that gets overridden in
the cascade, could reduce CSS down to a tiny fraction of its full size (never
mind comments and spacing). But would that give better results than loading a
big bloated and wasteful CSS file once and caching it for the entire site (or
many sites in a case like CDN'd Bootstrap)? Either way is one form of bloat
and waste or another.

------
tpetry
The problem with such a tree optimization is that you will only find a locally
best solution.

If compressor1 generates the smallest result in step 1 all other compressors
will only try to minimize this result. But maybe the compressor did something
which the other conpressors are not optimized for. So you'll find a lically
best solution for a starting point with conpressor1. But maybe it would have
been better to start with compressor3 because it's result is smaller after
step 2 than starting with compressor1.

------
SZJX
This just makes no sense. If somehow the minimizer wasn't able to further
process it, maybe it's just because it was already quite thoroughly processed
and you will loss some information if you continue? That's totally unsound
approach. What does that extra fee bytes of saving do to loading time anyways?
Probably very little, yet you risk very real degradation and broken behaviors.

Not to mention the author even admitted to not being very proficient in CSS
and doesn't even want to learn JS because he "dislikes it too much"... The
whole description of the process is basically dragging something on out of
very little substance.

In general good laughing material though, just as he acknowledged at the end
of the article, I guess.

------
vmarsy
This is pretty cool!

> Are the current CSS minifiers correct?

> I handle crashing minifiers, as well as ones that loop forever.

It could actually be useful to know the exact css before running the minifier
who crashed/got stuck. One could check with a css validator if this is correct
css in the first place (if not, one of the previous minifier screwed up) , and
if so, inform the minifier's maintainers of their tool crashing with this
particular valid input.

~~~
simcop2387
> It could actually be useful to know the exact css before running the
> minifier who crashed/got stuck.

I was actually thinking this could be used to speed up computation a bit too.
If you're going to add a set of minifiers to the end of the chain, caching the
intermediate results (really just the last one) would let you avoid
reprocessing from scratch each time you need to do cssnano | cssnano | csso |
*, I don't know if it does this now but the description didn't talk about it
either. That'd let you look at the chain and find where a mistake propagated
from.

------
dullroar
"IT...COULD...WORK!!!" \- Dr. Frederick Frankenstein

Cool idea. Especially not reinventing wheels but chaining them together
instead. Good work.

------
bgirard
This doesn't explore where the additional savings are coming from so that they
can be contributed back into minifiers.

~~~
remy_luisant
Pay me and I will do this. Otherwise I have to do my day job and my project
that I will use to survive when my job finishes.

I will even learn CSS and JS to a degree where I can contribute the patches
myself.

------
znpy
I wonder what the impacts of (re|)minification is on actual network
performance.

For example: consider a minified file x. X is likely getting gzipped when
served. Is the reminified version smaller than the original? Same size?
Bigger?

You might expect the obvious answer, but did anyone do the actual
measurements?

~~~
remy_luisant
It is a plan for a followup post to have that.

------
martijndeh
I'm curious though, how do these minifications, or CSS minifications in
general, affect runtime performance? Reducing the file size is great, and a
goal in itself (though brotli is more important where gzip is supported), but
another important goal is rendering the page fast. Some CSS selectors are
faster than others, is this something minifiers take into account? Or is this
not significant at all?

~~~
Etheryte
Changing selectors would be an unsafe transform (how could the minifier know
that the new selector matches the DOM?) and it's not related at all. Even if
it was, selector speed isn't really relevant, the changes are way too
miniscule.

------
sAbakumoff
>> There is a catch: Remynification takes forever and may break your CSS, even
though this isn't really my fault.

Stopped reading after this line.

~~~
beefsack
It's an interesting experiment, not some sort of production recommendation.

~~~
sAbakumoff
Yes, of course, but to me this phrase means: "I conducted the experiment and
you can reproduce it, but it can run forever and break your stuff". That
hardly sounds like an enticing introduction.

~~~
remy_luisant
It isn't meant to be enticing, but factual.

It will break stuff. It will run forever. The software that I committed works
in theory, but I don't think you should actually deploy it in its current
form.

If you aren't interested in some playful carnage on a slow news day, no
worries. Different strokes, eh? :)

~~~
sAbakumoff
Also "this isn't really my fault" might be a buzzkill.

------
SFJulie
Looking at modern websites: very few use the Accept-Encoding: gzip of the HTTP
1/1

This could even «minify the served CSS» a lot :)

(ok client side it will always be as big, but the server side is often paying
its traffic and the client side may too so it reduces traffic anyway and means
great savings).

For public static websites, the savings induced by gzip compression totally
justify to not use http2 when traffic matters.

~~~
icebraining
_For public static websites, the savings induced by gzip compression totally
justify to not use http2 when traffic matters._

What do you mean? You can use gzip in HTTP/2.

------
tambourine_man
Good… (read in Palpatine's voice)

Now rewrite all your selectors in optimum precedence and specificity for file
size. After that, remember that it will all go through gzip, so let's see how
reordering properties affect compression.

------
barrystaes
Filesize is not necessarily better performance. On todays hardware i believe
(but i have not tested this..) that opening a zipped 1MB file often is slower
than just opening it directly, basically thats just shoving it in RAM.

~~~
jo909
On the contrary, reading over the network or from disk (no matter how fast and
modern) is still comparatively slow and is nearly always the bottleneck.

Most non-embedded CPUs are more than fast enough to decompress every bit they
get from the network or disk without decompression becoming the new
bottleneck. There are other factors like latency, seeking, the minimum block
size, and it always depends on the application, but generally speaking reading
and writing compressed data should always be considered.

That being said, for CSS the benchmark should rather be the size after gzip or
other commonly used HTTP-Stream compressions, which are almost universally
used anyway.

~~~
rocqua
Compression and HTTPS don't play nice together though. So in cases where every
byte matters, and you can't get rid of HTTPS (an admittedly rather niche use
case). This minification still has some uses.

~~~
jo909
If you are talking about the BREACH vulerability, CSS won't reflect user
input. The attacker is not able to observe thousands of slight variations of
the same file, which is how this attack works (simplified). Transferring
static files compressed over HTTPS is completely safe to my knowledge.

So maybe if you inline your CSS into the page this comes into play.

Also I never said minification is useless, only that you need to compare
plain+gzip vs minifier1+gzip vs minifier2+gzip to see the _effective_ size
reduction.

------
Galanwe
Minimizing the lossfull minifier from:

    
    
        #!/bin/bash
        rm -rf $1
        touch $1
    

to

    
    
        #!/bin/sh
        >"$1"
    

Your turn, reddit

~~~
TazeTSchnitzel

      $ ln -s /usr/bin/true ~/bin/cssmin
    
      $ cssmin < in.css > out.css

------
bikamonki
I'm sorry, I have to:

[http://motherfuckingwebsite.com](http://motherfuckingwebsite.com)

------
d--b
Remy, you forgot the y in remynification in the submission title. Cause it's
all about you after all! :)

------
snsr
> _If lossy CSS minification is a thing that we are willing to put up with_

I'm not, neat experiment though.

------
leni536
And what's the size difference comparing the gzipped css files?

------
ldev
Now I know why most of my colleagues in software development do not have a
degree - academy really, really closes your mind in some strange bubble and
shields you from real world...

This article - I don't even...

