The scientific notation one is a bug. Scientific notation isn't part of the CSS spec[1], and its not supported in all browsers.
I learned this one the hard way a few months ago. We ran into a flexbox bug in one browser which we worked around by adding some-rule: 0.0000001px instead of 0px. However, our minifier collapsed that using scientific notation, which triggered a rendering issue in a different browser due to the out-of-spec CSS. The whole adventure left me feeling like I'd travelled back in time.
"q" is also CSS3, incidentally, and its background is interesting[0]: it's a mostly japanese metric typographical unit[1], it replaces the point, and is slightly smaller: q is 0.25mm while pt is ~0.3528mm (precisely 1/72th of an inch which is 25.4mm).
"q" is not supported in the latest version of Chrome but is supported in Firefox. I'm debating using it on my site just to get a few headscratchers. Also to play off the weeb factor a bit as a joke. Thanks for introducing it to me.
Now to debate whether or not adding 3 lines of my CSS for a joke is too much.
"q" seems to be doomed to failure. If I were that interested in moving away from points and ems, why would I adopt arbitrary fractions of a millimeter when I could just use plain old SI units that are universally understood? It doesn't even save a byte at typical font sizes: 20q == 5mm.
Saving bytes in CSS was probably not the primary concern upon standardization in 1976. Note that the PostScript point (now the most common definition) is younger than that. Common usage doesn't always follow the best option that's available, as evident by many examples throughout history.
Furthermore, having a measurement that can frequently be used with integer dimensions is convenient, which is probably a large part why we still use points. 12 pt is a convenient font size, so is 4 mm (technically 4.2 mm, as 11.8 pt would be 4 mm), but while 10 pt is still convenient, 3.5 mm is less so. Smaller units lead to more useful integer values (within reason, of course).
> It doesn't even save a byte at typical font sizes: 20q == 5mm.
Typographers apparently use sub-millimeter precision for their sizes e.g. across 9 use cases Aicher had a single integral-millimiter font size[0]. The quarter-mm seems to have been the base multiplier suggested by DIN 16507-2, though it apparently also suggests 0.1 or 0.05mm when even more precision is necessary.
> Scientific notation isn't […] supported in all browsers.
This has tripped me up many a time when I've created CSS colour strings (mostly for <canvas> use) by concatenating Strings and Numbers in JavaScript. When a Number gets small enough, it ends up in scientific notation, and the CSS parser rejects it.
> I'm guessing that at nine nines that is pretty much a one anyway and it would not even change a single pixel on the screen.
There used to be a bug with flex-wrap: wrap; where an element would wrap to the next line while it should have fit. You could fix it by instead using width: 25%; use width: 24.999999%; so it would be 25% on the screen but it would fix the problem so it didn't wrap to the next line. So you should look out with this.
Unfortunately every time I read something about minifiers I got the feeling that people are optimizing the wrong problem.
If you gzip data over the line it's already compressed. So minifying your stuff will only help you a little.
The problem is on the client side. You can compress what you like but if the browser starts dropping frames because it has to compile/handle a ton of Javascript and CSS then minifying doesn't help the end user.
> If you gzip data over the line it's already compressed. So minifying your stuff will only help you a little.
For small files you might be mostly correct, but for larger ones min+compress can product much better gains than compression alone.
IIRC the algorithm used employs a rolling compression window, and can only match strings of tokens whose distance apart is smaller than that window. IIRC the default window is 8KBytes and the maximum is 32KBytes. Even if you use the maximum at the expense of CPU time that isn't going to cover many large files. Minifying increases the effective range of the compression window, each match is shorter but you will find more matches and usually this balances out in a way that benefits the compression result.
It isn't quite that simple in reality as there is huffman encoding and other tricks in the mix. This means that even for inputs smaller than the compression window you may see some benefit as minifying can reduce the input data's alphabet significantly.
Ignoring the "why it helps", it is easy to show that it does help in a great many real cases:
In this example the result of min+comp is less than 40% the size of the result from compression alone.
For completeness, minifying alone achieves less than compression alone:
-rw-r--r-- 1 ds ds 267686 Mar 16 21:30 jquery-3.2.0.js
-rw-r--r-- 1 ds ds 79201 Mar 16 21:30 jquery-3.2.0.js.gz
-rw-r--r-- 1 ds ds 86596 Mar 16 21:30 jquery-3.2.0.min.js
-rw-r--r-- 1 ds ds 30023 Mar 16 21:30 jquery-3.2.0.min.js.gz
One further factor is CPU time consumed on the client decompressing and parsing the content but this is likely to be insignificant compared to the network or local IO time, if a device's CPU is under-powered enough that this is significant then it is unlikely to be able to run the decompressed code with useful performance.
Most of the gains in there are from stripping out comments. That plus whitespace removal gets you most of the benefit. I don't think the parent was advocating for dropping minification completely, but investing massive effort when you're already at the crest of the curve.
Stripping out comments would be one, but eventually remove all useless code and optimizing it using tools like Google Closure Compiler is way more effective in most websites that use a single bundle for everything.
When the subject is CSS, dead code removal is a way more complex problem, and only possible if your usage falls within certain constraints. Best bet is a component system with scopes styles that ensures you are only loading what is required.
I had an article about that too. If you have to do just one, you should go with zopfli or brotli instead of minifying. Having both minification and some kind of compression on top does help the file sizes.
> So minifying your stuff will only help you a little.
True, the difference between 10KB compressed and 7KB minified+compressed is negligible for your visitors, but it still takes 30% off of your traffic bill.
One massive thing minifying does is dead code elimination (slightly less applicable to CSS but it still applies using some build stacks)
We can build a "prod" version of the app and the minifying process will drop all the debugging code as well as any unused or uncalled functions from the output.
What JS Minifier do you know that does dead code elimination?
I would have thought that understanding what functions of a dynamic language that can be safely removed would require parsing/AST analysis beyond those found in the typical minifier.
I use uglifyjs and it does a pretty good job of it.
We use blocks like this throughout our codebase:
if (process.env.NODE_ENV === 'development') {
fancyDebuggingFunction(stuff)
}
// ... some code later
fancyDebuggingFunction(var) {
/* do debugging things here */
}
And when combined with some stuff that sets the process.env.NODE_ENV (not sure how that part works honestly, never really looked into it) it will remove not only the if statement, but also the function if it's not called anywhere and not exported.
Throw in Webpack 2's import/export bundling stuff, you can exclude whole modules which you don't use in production which can really reduce the size of your code.
And moving forward if the JS ecosystem can ever get a handle on a good way to move to import/export by default, you'll start seeing massive gains in this area by being able to strip out any parts of a library that you don't use.
Also, I believe most widely used minifiers use AST parsing to do their work.
Closure compiler, while trickier to use than the other options, will do dead code elimination, constant propagation, and other classic whole-program optimizations.
I have seen a script which was 250kb gzipped, but 125k minified+gzipped.
It was a script embedded to the other people's pages (and yes, it delivered substantial functionality, it was not just a tracker), so minification saved a lot of traffic/money for the company.
Minifying also speeds up decompression, because less data has to be produced by the decompressor. Compression and minifying are really different optimizations, as the minification does not need to be reversed. So each one has benefits.
-) Less total length means lexing+parsing will be a bit quicker
-) shorter class names will also mean a lower memory consumption because of shorter strings, and ideally fewer allocations if some pooling or smart allocator is used
But those points can probably be completely ignored, since JS is a way way bigger factor.
Does minification speed up parsing (less characters to tokenise)? If so, then minification+compression would be better than compression alone as it would make up a bit for the time spent decompressing.
I am not a security researcher, but I think you could keep the benefits of both compression and security, as long as you're careful on the server side:
Say you have a document structured like [boring data] [secret data] [boring data]. I don't know if any existing compressor lets you do this, but the gzip file format (really the 'deflate' format used inside it) allows you to encode this (schematically) as follows:
where each || is i) a chunk boundary (the Huffman compression stage is done per-chunk, so this avoids leaks at that level), and ii) a point where the encoder forgets its history - ie, you simply ban the encoder from referencing across the || symbols.
If you wanted, you could even allow references between different "boring" chunks (since the decoder state never needs resetting), just as long as you make sure not to reference any of the secret data chunks.
Edit to add: Also, if the "boring" parts are static, you can pre-compress just those chunks and splice them together, potentially saving you from having to fully recompress an "almost static" document just because it has some dynamic content.
The other benefit is from combining files and reducing the number of http requests. Minifiers are really needed for that, but the do make for some nicer development workflows.
> The other benefit is from combining files and reducing the number of http requests. Minifiers are really needed for that, but the do make for some nicer development workflows.
debatable with HTTP2 . Furthermore, separate files are easier to cache. If one of them doesn't change it doesn't have to be loaded again. That's my experience with bundles, especially when one uses asynchronous module definition instead of babel, webpack and co.
What about cache expiry? Minfiers can generate a hash and tack that on to the file name so it's cached forever. With http2 can you do this without the back and forward conversation?
Number of HTTP requests is not a concern with HTTP2 server push and multiplexing. In fact it's usually better to have 2 fairly sized files that can be downloaded in parallel rather than 1 large file.
probably not true, since most http/2 implementations that I know of use time multiplexing, which means that only one element at a time can pass, so the time is exactly the same.
I mean if I split a file in 10 exact pieces or if I split two files in the exact same 10 pieces as well I still have the same data.
(Edit: Well basically two files have mostly more data since they both might contain a BOM or so)
Don't mean to squash any enthusiasm, but these types of 1byte optimization savings don't really have real-world benefits due to over-the-wire compression like gzip and Brotli.
A more interesting problem to solve, I think, is that of optimising CSS rules for browser rendering.
I think that there is merit in designing a minifier that is explicitly designed to optimise the gziped size rather than the uncompressed size.
Things like:
* Rearrange rules within the file to put similar rules within the sliding window.
* Rearrange rules so that tail of the last declaration of one rule and the start of the next selector create the longest possible common substring.
* Rearrange the order of declarations within the rules to maximize the length of common substrings that span two declarations, ie ": 2em;\nbackground-color: rgb("
I'm working on one, though I'm not sure if it will ever see the light of the day. I have four months of free time and if someone would feed and house me for that time, I'd do it and open source it.
Any sponsors? No? Didn't think so. Not even you, big G? Aww...
For now some minifiers do sort the values, which helps.
I partly agree. Though removing one or two bytes more than another minifier doesn't really matter that much, what matters is being able to deduplicate CSS as well as doing the usual whitespace elimination. SASS and SCSS seem to have a bit of a problem with duplicated CSS.
It's funny you should say that. I'm curious, do you have an example?
I'm engaging in this ugly probably unsightly (but helpfully quick and maintainable) practice in a project with a short developmental cycle right now, and I've yet to have any issues outside of temporarily forgetting that I have already globally defined a specific style or enclosed a style I thought I'd left global.
(It's a corp. annual report -- that my team got tasked with as a favour -- so it has some repeating styles, and others isolated between pages)
One of the main reasons these optimizations happen is actually to make compression better. If you have a mix of px/pt/cm/mm in your stylesheet, it's more than likely that making them consistent (in their smallest possible form) makes them more uniform, making them more compressible.
I used to have a doc with actual numbers, but I've since lost it. If I dig it up, I'll link it here.
Anybody know if the transparency one is actually a desirable optimization? Iirc, you might want to assign a color to your transparency so it's not shifting hue as you fade it in through CSS transitions, animations, or JS.
Author of crass here. That's interesting: if you have a specific case where the browser doesn't do what you'd expect, I'd love to see it in a Github issue!
I was thinking of the fact that transparent is supposedly an alias for rgba(0,0,0,0), but this doesn't seem to cause an issue when I try it out, in Firefox at least.
crass is doing some really wonderful stuff here -- I'm impressed!
It's very interesting, however, that no one minifier is a consistent winner in these test cases, and that running CSS through multiple minifiers is actually, potentially, not all that crazy. (The very debatable real value in doing that notwithstanding.)
I appreciate the compliment! (author of crass here)
I've mentioned it before, but it's really not a great idea to use multiple minifiers. Minifier bugs can get nasty, and using multiple minifiers exponentially increases the likelihood that you'll encounter some weird or broken behavior. Make sure to test thoroughly.
I have a slightly-related question for those of you familiar with Webpack, css modules (css-loader/style-loader), and perhaps React as well: is there any reason not to use the 'default' approach where the styles for the components are simply inserted in a <style> (with unique, generated classnames)?
To be clear: I don't mean philosophical reasons. I personally love letting javascript deal with the 'cascading' part and I don't have a problem with the idea of having styling embedded in the final page.
What I'm curious about is if this has any kind of negative impact on performance, bandwidth, etc. Because the CSS is loaded on the component level, and because Webpack 2 does tree shaking, the page will be guaranteed to only contain CSS for the components that are on the page. And if I'd 'lazy-load' parts of the app, I'd get that benefit for my CSS as well with no extra effort.
On the other hand, any benefits of having a compiled (and hopefully cached) bundle.css are offset by the need for an extra request for the css file, as well as the very likely situation that there'll be a bunch of unused css in that bundle.
Am I missing some drawback to the above-mentioned approach?
When you're using a loader, the CSS still exists, it's just a big string in your JS bundle. By default, I believe css-loader/style-loader will use cssnano to minify the CSS within your bundle.
What will be very interesting in the coming years (as the work gets done around it) is "full css" optimization. That is, when you know you have all of the styles for the whole page available to the minifier. If the minifier knows that no other CSS is being loaded, it can do a lot more work to remove and merge rulesets. In the case of styles bundled with Webpack, common CSS could be reduced even further, after tree shaking has taken place.
In the long run I think we're more likely to end up with a full js-based styling approach that, similar to JSX, might look like CSS but really directly styles individual nodes and 'manages' them.
The tricky part is handling things like hover states and pseudoelements. I think scoped CSS and shadow DOM (and other new techniques) will probably be the key to making this work long-term.
<style> tags in the middle of the DOM often have a rerendering penalty (most browsers force an entire page rerender each time they encounter one).
In a past life a website I worked on had a huge browser paint performance and content flash issue that was eventually cleared out by moving all the styles out of <style> tags in the DOM.
Fair point. Once packed into the <head> is better.
There's still a care to be taken when using <style> tags in development. I know developers don't always respect development performance (because it's just development), but that performance can still matter: to you in your debug cycle time, and also sometimes bad performance in development masks bad performance that will impact production (and developers don't notice it because it "always performs that way when I test").
For that reason, bringing the aside somewhat back on topic, I often prefer to use minified stylesheets/JS in development and trust sourcemaps to do their job when I need to debug them.
Embedding css into a webpage forces this css to be loaded every time. Even if the css only contains what's needed for the page, it can still be a lot. Caching it is just common sense. I think if you try and calculate, there will be a lot of bandwidth saved if you separate css into a file.
Not true. The CSS is inlined as strings in the js file, and benefits from caching just as much as the rest of your templates (this is for single-page apps).
CSS is a far bigger spec than anyone knows. Did you know that you can put dots and spaces in your CSS classnames? Bonus points if you can figure out how to escape characters in CSS identifiers ;)
Don't forget that gzip/brotli only affects the shipping time. This would be the process:
1. Server: check Accept-encoding header for gzip or brotili support
2. Server: compress either brotli or gzipped file, or fall back to a raw file
3. Server: send data to client
4. Client: receive (and decompress, if not raw)
5. Client: parse (big) resource
Also, compression through uglifying/minifying improves parse speed, which is really helpful on (old) mobile devices. Adding compression through gzip or brotli introduces additional overhead, because the uncompressing step will be in-memory and stalls the processing of the file.
No, you don't need to waste CPU cycles on compression for each connection. You can store the .css.gz on the filesystem along with the .css and have the webserver pick up the appropriate file based on Accept-encoding.
That way you can precompress with the slowest compression options.
Of course, there are multiple ways to optimize it (like storing stuff at a CDN), but I'm pretty sure 95% of the people do not precompress their assets.
If you have a build step (you probably do), in most cases it's as simple as running `npm install --save` for your minifier of choice and adding one line to your build script.
Even if the difference is minimal, it could mean the difference between, say, three TCP packets and four, which adds up for users on high-latency connections.
If folks tend to use some higher level abstraction (isn't that what SASS and LESS are?) maybe it makes sense to provide a new way to encode the information in CSS. Similar to how WASM is supposed to be easier to parse than JavaScript, right?
Remy: I'd suggest posting a CV and linking to it from this post. I looked and couldn't find one anywhere on your site; you'll get a lot more qualified interest if people can find out more about you than just a few blog posts.
Not like it would lead to anywhere either, you know. :P
Two years of teaching university, MSc in computing, love of automation, combinatiorial optimization without any significant amount of deep math skill, circuit design, gate array stuff, low-level CPU optimization stuff, hardware counters, microcontrollers, SQLite, Julia language, C, shell, a bunch more programming languages, basic CSS and HTML, Mustache templates, Linux. No JavaScript. The whole of luisant.ca is done by hand and from scratch, so you can see what I can do with that. Also statistics.
I'm uncommonly silly and not at all serious, while still standing up for what I believe is right, even if everyone disagrees with me. I do integrity though and while I can stand my ground, I will yield to good evidence any day.
I wear goofy clothes to work, usually very colourful stuff. Don't expect conformity.
And I have no love greater than the one for the cause of nature conservation, and all research that goes with that.
Man, just post a link to CV like you're advised and let good things happen to you. If they don't, you lose nothing, but if they do, you got something out of your effort.
1. I am not interested in jobs that focus on JavaScript, but I'd never say "No JavaScript".
2. when people ask for your CV, post a link (or if you're concerned about privacy, solicit requests via PM/email).
3. Your integrity, sillyness and attire are mostly immaterial to the job hunt. It's most appropriate for folks to make this kind of assessment during an in-person interview.
4. what is all this "nothing will come of it" business? If you think you can't get hired as a result of an HN post, I think you're sorely mistaken. Capitalize on these fifteen minutes. You've created some original, interesting content.
The traffic you're getting to your site won't last, so strike while the iron is hot.
> "Your integrity, sillyness (sic) and attire are mostly immaterial to the job hunt."
this sounds like drab corporate thinking and i disagree wholeheartedly.
the attributes mentioned show his personality and warmth, and indicate the possibility of an interesting person and a creative thinker. as a hiring manager, my ears perk up at such things.
You're being silly, I'm a self-taught Python coder with a degree in agricultural engineering, a sprinkling of C, Linux, Lisp and statistics, almost no JavaScript, I'm headstrong and I dress like shit, and I still managed to find interesting opportunities in the job posting threads here.
I learned this one the hard way a few months ago. We ran into a flexbox bug in one browser which we worked around by adding some-rule: 0.0000001px instead of 0px. However, our minifier collapsed that using scientific notation, which triggered a rendering issue in a different browser due to the out-of-spec CSS. The whole adventure left me feeling like I'd travelled back in time.
[1] https://www.w3.org/TR/CSS21/syndata.html#numbers