What would you say, if I told you - delivering JS is a super simple task while delivering CSS is way, WAY MORE COMPLEX? And I've just said it!
How many ways to define styles you know? I could name 3:
inline styles. Basic and most inefficient way.
<style>s. The HTML way to define CSS. Better, although not cacheable.
.cssfiles. The HTTP way to define to deliver styles for the page, and the only one cache-friendly one.
But the first two options do not require a separate
.css file to work - they could be delivered to the page using
.js as transport.
This is what
CSS-in-JS is -
css, defined and delivered in
Even the third option does not require
.css file - the link to the
Let me be completely honest - CSS-in-JS usually sucks from the "speed" point of view - JS has a significant cost, and delivering the same styles via plain CSS would be much more efficient and should be preferred at all times if you are looking for the best user experience.
However, there are two moments, where "real" CSS-in-JS just shines:
- complex auto-generated styles(like dark mode). As long you can autogenerate them in-place, not create 100500 different styles using SASS loop (or your own hands). Sometimes, rarely but still,
css-in-jsmight be much much more compact than a pure
- SSR. Where you can inline styles next to their usage and, actually, creation (in terms if StyledComponents). As a result, you will be able to render a page before fetching real style files(that would take a while). And even more - before parsing all styles on the page(better say - downloading), as long as styles will be interleaved with HTML in a most efficient way.
It's like - defining styles for
header just before
header, as well as defining styles
footer just before
footer - so you don't have to load anything "extra" to display the next block of HTML. That's makes (HTML) rendering process more streamlined and efficient on slow connections. I mean FAR more efficient, and that's matters! First load matter!
And we need the same goodness for a "normal" CSS
CSS is a very nasty thing - while we are loading our scripts as
defer - CSS is always sync. And thus always the highest possible network priority.
As long as without proper CSS you can't properly display HTML - everything, just everything would wait for it to load first. Let me cite one image from Defer non-critical CSS google article:
And there is two way to make this situation better:
- code split CSS files, so you will need less CSS to download here and now, and could spend less time in networking. Always working, but has some limitations, which might prevent this variant from being adopted in your project. Like it does not worth it.
- inline "used" or "critical" CSS, which is usually just a few (dozen) selectors required to display content above the fold.
The problem with CSS and code-splitting is not code-splitting, but CSS. I hope you know what is "rule specificity", when a selector defined "later" has more power than a selector defined before.
Everything was simpler with one big
styles.css, but one you shred it into 5 different files - they could be assembled in an absolutely random order, causing all "css related issues" you ever heard about.
to use CSS and code splitting you have to be ready handle your CSS code more responsible, and never rely on the correct rule declaration order between different files.
However - keep in mind - CSS code is usually quite compact, comparing to JS, and does not require much time to download and parse. Code splitting for CSS might not be needed
Anton KorzunovSize matters? You should code split big things, like..
- JS: 25k lines -> 1Mb/250kb Gzip -> 500ms to download -> 7ms parse + 2 compile + 250ms to execute = 760ms 🐳
- CSS: 14k lines -> 400kb /50kb Gzip -> 53ms to download -> 4ms to parse = 60ms 🦋
💡CSS is not a big thing!22:27 PM - 30 Sep 2019
Is something we want. Is something very, well, delightfull? And is yet another problem. To be more concrete - Google Web Fundamentals Guides, in their Inlining critical CSS, are not proposing any feasible solution. Let's move on
According to the
Inlining critical CSS there are 3 node libraries, which could help you "extract" a
Critical CSS for a given HTML:
- plus modpagespeed for your server to do some "optimizations". Which ones?
For given URL and CSS - produce "critical" CSS. Have no idea how I could use it with JAM stack or for SSR.
Uses puppeteer to detect window size, and thus very slow.
A bit more complicated version. But works in the same way.
While both approaches could provide very fine results - their targets are mostly static sites, as long as there is no way you could use them in runtime.
However, these guys could handle runtime. However, they could just inline
.css you will point finger on. That's not a Critical CSS, and, of course, not "extraction".
Ensure that the CSS you are planning to inline is genuinely critical to your web page and you are not inlining everything. source
So - how to obtain the
CSS-in-JS level of critical style extraction, but for normal CSS? The thing you will also need for all those
css-in-js, which ends in
.css, and which I would gently recommend you to use?
Just check which styles were used.
Bundler and framework independent CSS part of SSR-friendly code splitting
css files from the given HTML, and/or inlines critical styles
Supports sync or stream rendering.
Read more about critical style extraction and this library: https://dev.to/thekashey/optimising-css-delivery-57eh
🚀Super Fast - no browser, no jsdom, no runtime transformations
💪API - it's no more than an API - integrates with everything
⏳Helps preloading for the "real" style files
Works in two modes:
🚙inlines style rules required to render given HTML - ideal for the first time visitor
🏋️♀️inlines style files required to render given HTML - ideal for the second time visitor (and code splitting)
Critical style extraction:
🧱will all all used styles at the beginning of your page in a string mode
used-styles are working quite simple - you are giving them you
dist directory, they scan it using
PostCSS, and thus creating the database of styles might use.
Then you are giving them a piece of HTML -
stream - and they would tell you which style, from which file, was used. And in which order you shall declare them.
Relax, I am joking - of course, the library would handle everything out of the box, including duplicate prevention. And in case if using React's
renderToStream - the result would be interleaved with HTML (however correct rule order would not be guaranteed in this case).
Everything works out of the box, except a few tricky moments...
You still have to load
.css files. As long you inlined only a small part of all styles, which might be required just after the page start.
Critical CSS is good for FCP(First Content Paint), but might be bad for the application as a whole.
the inlined CSS will result in some extra page weight every time a user loads your website. For example, if there is 30kB of inline CSS on every page of your website then 10 page views by a single user will cost the user 300kB. It may not sound like a big deal but data is expensive in some parts of the world (and on some 3G/4G data plans). source
Loading these "missing" CSS would be triggered by your bundler automatically, especially in case of code splitting. And those styles would be loaded at maximal network priority, as long as they are CSS, delaying JS start and making hydrating death valley deeper - you have to manually disable, usually by creating a
<style data-href="real-style.css" /> to indicate that you handled external style manually.
used-styleswould tell you which CSS files was used
Critical CSS is required only for the FIRST visit. Inlining styles second time is a waste of time. You already loaded "real"
.css files, and their reloading would not consume any time - everything is cached.
In the ideal situation, you have to track (on SSR) which files you already send to the client, and handle them in the "old" way.
you can control which files should be inlined and which should not
However, this is not so easy and might require some server infrastructure, especially shared cache. (and cookies, at least session cookies)
That's a great question!
- Critical CSS Extraction would work only if you have HTML, so you have to have SSR
- just one file inlining would work any SPA, but it might be not a great idea to inline big files into your HTML - big files are better be cached, as well as inlining small files - they might not change anything - reducing TTFB might have more impact in this case.
Need an example? the urge uses
used-styles to make your experience better, however not covering some tricky cases I've listed above.
and of course 👇