Proposal: let’s have something like
<link rel="stylesheet" href="..." async />
for loading and applying stylesheets without blocking the page rendering.
User story
I, as an application developer who cares about performance, want to have a non-render-blocking way to load non-critical CSS.
Critical CSS is the part of site styles that are required for the very first rendering. This could be e.g. CSS for above-the-fold content. Non-critical CSS is the remaining styles.
Specific use cases
In a news site: load and apply styles for the page layout and the news content + hide the comments/ads blocks. Then, without blocking the initial rendering, load and apply styles for comments and ads.
On a landing page: load and apply styles for above-the-fold content + hide the content below the fold. Then, without blocking the initial rendering, load and apply styles for below-the-fold stuff.
Something like:
document.addEventListener('DOMContentLoaded', () => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = '...';
document.head.appendChild(link);
})
Pros:
Cons:
media param + JSSomething like this:
<link rel="stylesheet" href="..." media="only fake-media" />
<script>
setTimeout(() => {
document.querySelector('link').media = 'screen';
}, 0);
</script>
Pros:
Cons:
<link rel="preload" onload>Something like this:
<link rel="preload" href="..." as="style" onload="this.rel = 'stylesheet'" />
Pros:
Cons:
<link rel="stylesheets" /> (browsers might give <link rel="preload"> and <link rel="stylesheet"> different priorities)<link async>Something like this:
<link rel="stylesheet" href="..." async />
Pros:
<script async /> declaration, so is easy to remember<link> in older browsersCons:
async attribute which won’t make sense for non-stylesheet <link> tags. (Through there’s already the as attribute which works only with <link rel="preload" />, and that’s fine.)<link rel="stylesheet-async">Something like this:
<link rel="stylesheet-async" href="..." />
Pros:
async attribute which only makes sense for some relsCons:
rel (seems like a huge addition)async attribute (because it doesn’t match <script async>)I’m personally in favor of solution A.
Also, to explain the “it’s a hack” cons. These hacks might look OK for folks with web background (including myself), but, in my experience, they’re a part of “death by thousand cuts” for non-web developers. That’s why I prioritize this. It’s not cool to have conversations like “How do I do this _[basic stuff]_?” – “Oh, there’s no proper solution for this, use one of these hacks depending on your requirements” – “Uh, okay.”
cc @jakearchibald @igrigorik @yoavweiss
You may add link tag with ref preload to the top and link tag with ref stylesheet to the bottom, without async attribute.
It should work same way.
I am confused by this, this already exists years ago https://www.filamentgroup.com/lab/async-css.html
@montogeek main idea — don't use js and use only one styles definition. Actually idea is quite good.
Also http2 push kinda solve this problem too. Almost.
One another question: rel="stylesheet-async" is not backward compatible, so is will take it's time before anyone may really use it.
It should work same way.
Just tested with Chrome stable, and this didn’t work. Do you have a reproducible example?
Also http2 push kinda solve this problem too. Almost.
Server push would solve the preloading issue – but AFAIK the browser won’t apply the pushed stylesheet without the <link> tag. So that’s not a complete solution unfortunately :/
One another question:
rel="stylesheet-async"is not backward compatible, so is will take it's time before anyone may really use it.
Great point, I missed this! <link rel="stylesheet" async /> should be backwards-compatible though. I’ll add that into pros/cons.
Right now:
…content…
<link rel="stylesheet" href="…"><script> </script>
"content" can render before the stylesheet loads in every browser except Chrome (ticket).
This means you can achieve async stylesheet loading using a preload plus a stylesheet at the bottom of the document. You can also incrementally load styles by placing stylesheets just before the first element that needs them.
The above is more versatile, and it'd be nice if it was standardised (along with a change to Firefox so the <script> </script> isn't needed.
An async attribute on <link> seems sensible, but I don't think it's as useful as incrementally-applying CSS.
In general I agree with @jakearchibald in that this is a decent idea but might not be most useful to incrementally apply styles. I'm wondering if a lot of the questions that might be brought up by this thread regarding the async attribute are related to https://github.com/whatwg/html/issues/1349.
"content" can render before the stylesheet loads in every browser except Chrome (ticket).
FWIW, under existing solutions, (B) doesn't always work; for example, see https://github.com/whatwg/html/issues/2886, which Firefox has just about solved but it is not in stable yet. Regarding (C), I believe @wanderview told me that Firefox's preload implementation is currently turned off, so I don't think that'll load. And regarding the priority associated with preloads, the HTML Standard mentions that the priority should be the same as the as resource type, but I'm not sure how many browsers actually follow this.
Thanks for the feedback! Yup, rendering the page until a <link> is found should be more useful.
Closing this in favor of #1349 as it covers standardization of this <link> behavior :–)
Most helpful comment
Existing solutions
A. Element added with JavaScript
Something like:
Pros:
Cons:
B. Non-matching
mediaparam + JSSomething like this:
Pros:
Cons:
C.
<link rel="preload" onload>Something like this:
Pros:
Cons:
<link rel="stylesheets" />(browsers might give<link rel="preload">and<link rel="stylesheet">different priorities)Proposed solution
A.
<link async>Something like this:
Pros:
<script async />declaration, so is easy to remember<link>in older browsersCons:
asyncattribute which won’t make sense for non-stylesheet<link>tags. (Through there’s already theasattribute which works only with<link rel="preload" />, and that’s fine.)B.
<link rel="stylesheet-async">Something like this:
Pros:
asyncattribute which only makes sense for somerelsCons:
rel(seems like a huge addition)asyncattribute (because it doesn’t match<script async>)