Critical CSS? Not So Fast(csswizardry.com) |
Critical CSS? Not So Fast(csswizardry.com) |
It boggles the mind that we style bikeshed over CSS. Is it honestly, truly, and pragmatically worthwhile to spend all this engineering effort manipulating, culling, tree shaking, modularizing our CSS? Pre-load that, inline this, async-load that, and what have we got for it? A web full of wiggling content, slow CDNs, dozens of requests, and an experience of a mis-timed, slow loading web of bullshit.
Deliver less CSS in the first place, be pragmatic, load it as one file when the user hits the website, from the same server they got the HTML from. Bam, lightning fucking fast, like it was before we invented all these new problems.
The brief flash of your half-styled website is useless to me. Block rendering until the CSS is done. I don't enjoy looking at half an interlaced video frame, or 100px of a 200px image, just show me the finished product.
The content wiggles and asynchronous bullshit is what makes your website -feel- slow, even if you managed to trick Google into thinking it loaded quickly.
You don't have to ever think about this stuff if you use a component-driven frontend framework like Vue w/ Vite (or Webpack).
The general design: you use one global .css file for your shared styles (app.css) and then each component has their own inline CSS.
Vite will automatically extract each component JS and CSS in tiny files and only load them when you visit a page with the specific component.
This all happens automatically and you can cleanly organize your project by keeping the styles close to the view template and logic (often the same file has the template, <style> and <script> which each get extract automatically into bundles). The .css/.js filenames match the component name which makes debugging easier too in dev console.
HTTP2 handles loading 20-100 small files like nothing and with large SaaS apps usually only have tons of components that rarely get used, so no need to load them all for every page.
If you had to think about this stuff, manage the tooling, and customize then it would be overhead, but otherwise with frameworks it's pure benefit.
That's such a tool complexity that most people don't need and probably shouldn't bother with for what is often a microoptimization.
I assume this is why a lot of pages do it: SEO over UX. If they cared that much about user experience the ads and tracking would be gone before worrying about CSS in this way.
I have good news for you: https://web.dev/critical-rendering-path-render-blocking-css
CSS is already a render blocker.
A lot of new approaches atomically separate the CSS and JS into dozens of smaller files that load in as the parts of the website that need them load in. Critial CSS is likely also a response to the issues that this approach creates.
As the article alludes to, they're ideas that makes total sense clinically, but falls short really quickly in practice.
If you use a static page this isn't as relevant. It can become significant for single page apps, especially if you dynamically load CSS.
I know I'm talking against industry opinion, but I don't believe browsers are the right platform to be loading things on the fly as the user needs them. It's 50-300+ms absolute best case, plus processing time. It's going to feel bad.
Is there a replacement for CSS Modules' "composes" keyword? Tailwind has @apply, but that defeats the purpose of a utility framework somewhat.
It seems to have a lot of the benefits of Tailwind but better integration with Typescript.
Assume that I know _nothing_ about Tailwind, sell it to me. I'd appreciate it. Thanks.
YES! It affects e-commerce conversion rates measurably.
Great point by the author: "make sure it’s the right thing to focus on." Hobby site? Don't care about FOUC. E-commerce? It's a game-changer.
> The brief flash of your half-styled website is useless to me
Not to the rest our users though! Are you on a 3G connection in a third-world country? If not your experience may be not be shared but every user.
My core industry is e-commerce, have been doing it for over a decade. Our fastest sites are the fully cached, single CSS file sites. You get sent a small amount of CSS and HTML, and you are off to the races. Javascript comes later, and is not necessary to operate the site until checkout.
What kills a sites speed for us is usually shitty CDNs and bloated assets, we don't have those. There isn't a single e-commerce platform popular today that I feel does this well, but that is because they are all enterprise platforms full of enterprise features.
The current best performant way to load JS is asynchronously as documented at https://web.dev/efficiently-load-third-party-javascript/.
And the best way to load CSS is with Critical Path CSS + Async CSS as documented at https://web.dev/defer-non-critical-css/.
The easiest way to generate Critical CSS is https://github.com/addyosmani/critical where you may suggest multiple resolutions.
I have found https://github.com/addyosmani/critical-path-css-tools to be a great resource to master critical path CSS which improves page render speeds. It helps build fast rendering sites, sometimes even sub-second renders given you have a low latency backend.
He says if you’ve got other blocking scripts / stylesheets in the head then there’s no point using critical CSS
Inlining critical CSS is madness for any project that is above a certain scale. Maybe you can do it for a single page, but in my experience the results are not worth the effort. You can make your site exceptionally fast without it.
Any page complicated enough to need critical CSS extraction is probably also going to take 50-100ms to render and transfer to the user. Just send the headers before you render and the client will probably have finished downloading them by the time you're done.
If clients are on a crappy network, critical css is even worse of an idea because they'll scroll down and get a broken page for 5 seconds
Every HTML file should start with: doctype, html tag, head tag, charset meta tag, title, stylesheet link tag. A couple of those tags are even optional.
Most annoying is fancy "loading" screen before page load.
Skip that fancy loading screen/spinner page and your site is already faster.
I build a lot of landing pages so there are very few multi page visits.
If you're passing your Core Web Vitals scores, there is no further ranking signal.
Transcribed from a 2021 Q&A with Google engineers
> beyond that point [of a good threshold for all Core Web Vital metrics], you don't get additional boost for reaching it even better. Like if you have your LCP at two seconds and you get it all the way down to one second, um, we've kind of publicly stated that that will not increase your ranking
I have found that keeping my code short and getting it all out in 1-3 HTTP request is the optimal performance strategy.
Is advice different than what you prefer "misleading?"
Anyway, he doesn't actually weigh in on marking JS with `defer` or `async`. And the article is directly contesting preloading the styles, given his note on race conditions with `media` switching. Moving the CSS before the `</body>` closing tag is genuinely a way to really defer your CSS.
> Why would we ever put non-Critical CSS in the <head> in the first place?!
To this point from Harry, I find myself skeptical that anyone's really going to want to do that. Getting layout jank / cumulative layout shift fixed when the main stylesheet is applied is a sisyphean task.
I have not run into layout janks when I have used critical path CSS. I'd look into other tools/settings while extracting the critical path CSS if this would happen to me.
That has to be a typo, right? Or sarcasm?
I weep for what we’ve all done to the beautiful speed and simplicity of the web.
Not disagreeing with you though - it's amazing how much bloat we've added. Nobody seems to get just how fucking fast the web actually is. Take a look at Figma or Linear if you want to see products that truly care about performance.
This script they use to load the async JS can also be racey and it’s far better to put the link to noncritical styles at the bottom of the page
Agree on the async JS part but I guess browser makers will solve it soon.
Like I said for large SaaS products you might never see 50% of the components on random settings and other less used pages. Some users only load a few index pages the first few times they visit so making it load faster is always a win.
Frameworks handle way more complex stuff that this. It's not overhead.
On hobby sites it doesn't matter. On high volume e-commerce it does.
Rewriting your JS to be async-ready can be a huge lift. Not every site pursuing frontend performance is ready to do that, much less has done it. I think this article certainly has an audience.
> I have not run into layout janks when I have used critical path CSS
Call me a skeptic :) if a user is able to scroll beyond the fold before the CSS loads, you've introduced CLS. Spoiler: they will always be able to.
Critical CSS and 0 CLS happens only when you create skeleton placeholder components for everything below the fold as well. There are no "tool/settings" that will do this automatically.
CSSWizardry targets performance engineers which is why I pointed it out. I would never post that for some Next.js or other new hotness blogpost which isn't focussed on frontend performance.
> Call me a skeptic :) if a user is able to scroll beyond the fold before the CSS loads, you've introduced CLS. Spoiler: they will always be able to.
I can't speak on code I haven't worked on or predict how users will react on UIs without looking into data. But I read this as excuses from providing the best UX to the end user.
> Critical CSS is a technique that extracts the CSS for above-the-fold content in order to render content to the user as fast as possible.
You can decide to put whatever you want into the style tag, but "critical CSS" explicitly refers to above the fold.
The Web Dev article might decide to define Critical CSS as about the fold but there are at least three ways of looking at Critical CSS
- Styles needed for above the fold
- Styles needed to layout a skeleton, and main content to avoid layout shifts
- Styles needed to layout the whole page
Above the fold is an arbitrary definition because it varies by device with different styles being required for different viewport sizes (at least)
The approach of scanning the rendering the page to see what styles are needed then extracting them into inline styles might work for small unchanging sites but it just doesn't scale
The only way I've seen sites maintain Critical Styles over time is to have a strong design system / patter library so they know what styles are needed to render the page and then split the styles between those needed for first render, and those needed on user interaction
> Critical extracts & inlines critical-path (above-the-fold) CSS from HTML
https://github.com/addyosmani/critical
Penthouse, the OG of Critical CSS generators
> Penthouse will return the critical CSS needed to perfectly render the above the fold content of the page
https://github.com/pocketjoso/penthouse
> Above the fold is an arbitrary definition because it varies by device
I mean, sure. But your critical path of CSS is never going to mean the whole page (unless it's a very short page). And skeleton components is objectively a completely different thing. Having definitions, even if "arbitrary," are important.
Whoever claims that sub second rendering isn't outstanding is just clueless.
After that you have 500ms to render your webpage. If you are lazy loading images, and most assets below the fold, thats a lot of time to get a page rendered.
You may take a look at my personal website (https://www.troysk.com/) which is hosted via Cloudflare so has CDN support and does sub second render on first load even though its image heavy.
Note also that the parent commenter mentioned being logged in to HN. You're serving a static page, which will always be fast. There are many aspects of HN's site that cannot be cached for logged in users, which will mean a higher TTFB.
> If you are lazy loading images
> sub second render on first load even though its image heavy
Images are not render blocking, and so won't be a part of this discussion. (Nor would I consider 1MB of mostly SVGs to be "image heavy")
Your site actually has room for improvement on performance. You could likely cut your overall page weight in half (not that it would do much, being default fast).
I'm well aware of Addy and Jonas' work…
In my experience trying to just extract and maintain ATF styles is impractical at scale as there 1,000s of viewport sizes, and large sites often have 10,000 pages – I've seen many a team try it and abandon it for something more realistic
I've used a broad definition of Critical CSS for a long time and will continue to do so as the narrow definition is impractical at scale IMV
Naming is hard. No more naming.
Switching between your markup and your CSS added friction to your flow. No more friction.
Avoiding problems caused by overly aggressive rules / cascade/ whatever is mostly a thing of the past.
I could go on, but if it’s not obvious, I absolutely love it. And the Tailwind UI project is 100% money we’ll spent, too.
That's kind but they have earned shedloads already (over $2m USD as of Jan 2021 [1]). That's on top of the $2.3m for the Refactoring UI ebook [2].
1. https://www.smalltechbusiness.com/monetizing-open-source-tai...
2. https://twitter.com/adamwathan/status/1289702466754211842
Tailwind removes (or wilfully ignores) the "cascading" from css. It breaks all good, old css design patterns on purpose - in favor of "widget-level" styling.
It also effectivly builds its own (extendable) style language via named classes, ending up somewhere between "semantic" and "micro-styling" - but allowing for refactoring as needed. It reduces CSS to an implementation detail.
For example, in "Agile Web Development With Rails 7"[1], a number of form fields are initially styled as:
... class="block shadow rounded-md border border-green-400
outline-none px-3 py-2 mt-2 w-full "
Later reactored to: ... class="input-field" via tailwind:
.input-field { @apply
block shadow rounded-md
border border-green-400 outline-none
px-3 py-2 mt-2 w-full
}
On the surface similar to defining a css class - but effectively scoped to your own code.In a small project, where you're publishing documents - tailwind might not be great - but in a large project, with many widgets it is a good fit.
Similar to how "movable code" (js on the client) might be a better fit than REST for web applications (as opposed to web pages).
[1] https://pragprog.com/titles/rails7/agile-web-development-wit...
It may be a longer read than you were looking for, in which case I’d say just try it on a small project next time you get the chance. It looks like it wouldn’t work, but it really does.
Also not to mention, you skip all the nonsense of the CSS in JS and the need of tools to extract the CSS from JS. Also other big win is that you can use with other projects not based on JS, I'm toying with a WASM app and use tailwind without issues, just run the tailwind bin on my app change (takes a second or less to generate the css file), the only downside here is the need of nodejs (you might be able to download the binary directly and run that, like I do on CI).
In my opinion, Tailwind is to CSS what jQuery was to Javascript. It's just the right level of abstraction that you need.
But you have to just try it for yourself and experience it. I'd recommend using it with VSCode's intellisense plugin.
Maybe you mean classes organized with a high level of abstraction that have multiple rules and are meant to be related to the document structure in a specific way?
Your observations reeks of incompetence. Are you seriously comparing a few bytes difference? The site loads more CSS from https://www.troysk.com/stylesheets/style.css
And hardly any JS? Can you even read code?
Many sites use static caching and then load dynamic parts asynchronously. The goal is to provide the end user a usable UI real fast. There is also Fastly and other new caching solutions if you are interested to know more.
> Your site actually has room for improvement on performance. You could likely cut your overall page weight in half (not that it would do much, being default fast).
How does it benefit? Have users reaction rates become faster than 1s on the web? This is a site not a game.
Setting aside that you're wrong (your style.css is 1.7kB, HN is 2.2kB), that small difference is kind of my point. Discussions of if a quick FCP is easy or not should be based on a typical site. Tiny, static portfolio sites ain't that.
> And hardly any JS? Can you even read code?
Your site's entire JS weight is below 50kB.
> How does it benefit? Have users reaction rates become faster than 1s on the web?
Right. That's why I said, "not that it would do much, being default fast"
CSS: https://i.imgur.com/sgmlnkF.png
Not sure what you're doing at this point. Are you trying to make the case that your site uses a lot of JS...? The site's entire JS package consists of LoadCSS, some ScrollMagic calls, and a Google Analytics tag.
<div class="product">
<button class="product__add-to-cart">
...etc
BEM is a good example of what Tailwind is a counterpoint to.So i think using utility classes/tailwind for base/layout and still using named classes where it makes sense (like common reusable components, hover hiearchies, transitions/animations) is most practical approach. And using BEM as convention for the named classes is not a bad idea - certainly better than no system.
Also, if you handed me a project with that approach, I would run for the hills! How would you decide when to use BEM styling and when to use the tailwind classes? It'd be a nightmare.
The resulting css you end up with is usually super light only with special things, edge cases, hover groups and stuff TW just doesn't handle.
You could of course have a policy to never refractor - but then you might need to enforce alphabetical use of tw classes to more easily keep styles in sync accross your codebase?
I'd say that dropping the cascade, along with namespacing "classes" (through the build-step) is the main feature of tailwind. It's a departure from CSS - I don't think I'd recommend to mix and match.
I like CSS, but I also see how it's a complex tool that's often used poorly, even by experienced developers.
As for themeing - I'd say that is well supported within tailwind.
When you have any text on page like a heading - tailwind approach is for you to set ".text-xl .leading-tight .tracking-tight" ok fine. Your designer is then like. "Yeah on mobile the fontsize must be much smaller and thus tracking+leading bigger" (because these 3 are tightly related) then he will also want the heading even bigger on big screens (and decrease tracking+leading). Suddenly you are juggling 9 classes that also have some fixed value. So you look into Figma and see that on mobile the leading of this style of heading is 1.27. On laptops its 1.14 and on big screen it's 1.035. So what now - will you change the design or will you add new utility classes specifically for this heading?
So you realize the best approach is to create one class .text-heading-1 and use css - that solves all the issues and it's super easy to change moving forward when you need some fine grained responsive adjustment.
The great thing about TW is that it's very fine grained in html but on the other hand it's bad at being explicit and precise like css is. I think the reason people like TW so much is that huge chunk of web work is layouting. Flex here, justify there and add gap. No more pain to target/name element just because you need to add margin and flex.
Theming is OK to some point (like making dark version) but if you need to make broader changes between multiple themes it just crumbles.
In my experience, devs using tailwind will happily wait until there are 20+ classes before refactoring - on the surface this seems insane - but if all you're editing is single, re-usable components (eg: "text on a page" is in a custom "article" component) - it's no longer quite as crazy.
> Theming is OK to some point (like making dark version) but if you need to make broader changes between multiple themes it just crumbles.
You wouldn't make css zen garden with tailwind - you would make such sweeping themes at component level, where you can change style, behavior (js) and markup all together.
Again - tailwind isn't for document styling, but for building applications - that happen to feature html and css as implementation details.
And yet it's app developers i know that hate it and don't want to use it (they already have single file components). But agencies making super custom content websites seem to be all over it. https://www.awwwards.com/websites/tailwind/ These websites are css zen gardens if anything.