Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: a non-render-blocking way to load stylesheets #3672

Closed
iamakulov opened this issue May 8, 2018 · 14 comments
Closed

Proposal: a non-render-blocking way to load stylesheets #3672

iamakulov opened this issue May 8, 2018 · 14 comments
Labels
addition/proposal New features or enhancements topic: style

Comments

@iamakulov
Copy link

iamakulov commented May 8, 2018

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.

@iamakulov
Copy link
Author

iamakulov commented May 8, 2018

Existing solutions

A. Element added with JavaScript

Something like:

document.addEventListener('DOMContentLoaded', () => {
  const link = document.createElement('link');
  link.rel = 'stylesheet';
  link.href = '...';
  document.head.appendChild(link);
})

Pros:

  • Works

Cons:

  • It’s a hack, so it’s hard to promote this as a proper solution
  • Doesn’t allow the browser to start prefetching early
  • Works differently in different engines (AFAIK would still block rendering in Blink if the page wasn’t rendered at the moment of adding)

B. Non-matching media param + JS

Something like this:

<link rel="stylesheet" href="..." media="only fake-media" />
<script>
  setTimeout(() => {
    document.querySelector('link').media = 'screen';
  }, 0);
</script>

Pros:

  • Works
  • Allows the browser to start prefetching early

Cons:

  • Still a hack (which makes it hard to promote this as a proper solution for performance)

C. <link rel="preload" onload>

Something like this:

<link rel="preload" href="..." as="style" onload="this.rel = 'stylesheet'" />

Pros:

  • Works
  • Allows the browser to start prefetching early
  • Easy to add

Cons:

  • Might work slower than loading stylesheets with <link rel="stylesheets" /> (browsers might give <link rel="preload"> and <link rel="stylesheet"> different priorities)
  • Not that easy to add in React (and probably other frameworks)
  • A terrible hack

Proposed solution

A. <link async>

Something like this:

<link rel="stylesheet" href="..." async />

Pros:

  • Works
  • Allows the browser to start prefetching early
  • Easy to enable (and not a hack)
  • More or less matches the <script async /> declaration, so is easy to remember
  • Gracefully degrades to plain <link> in older browsers

Cons:

  • Adds the 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.)

B. <link rel="stylesheet-async">

Something like this:

<link rel="stylesheet-async" href="..." />

Pros:

  • Works
  • Allows the browser to start prefetching early
  • Easy to enable (and not a hack)
  • Doesn’t add the new async attribute which only makes sense for some rels

Cons:

  • Adds the new rel (seems like a huge addition)
  • Backwards-incompatible
  • Less nice than the async attribute (because it doesn’t match <script async>)

@iamakulov
Copy link
Author

iamakulov commented May 8, 2018

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.”

@annevk annevk added the addition/proposal New features or enhancements label May 8, 2018
@annevk
Copy link
Member

annevk commented May 8, 2018

@SilentImp
Copy link

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.

@montogeek
Copy link

I am confused by this, this already exists years ago https://www.filamentgroup.com/lab/async-css.html

@SilentImp
Copy link

@montogeek main idea — don't use js and use only one styles definition. Actually idea is quite good.

@SilentImp
Copy link

Also http2 push kinda solve this problem too. Almost.

@SilentImp
Copy link

One another question: rel="stylesheet-async" is not backward compatible, so is will take it's time before anyone may really use it.

@iamakulov
Copy link
Author

iamakulov commented May 8, 2018

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.

@jakearchibald
Copy link
Contributor

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.

@domfarolino
Copy link
Member

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 #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 #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.

@iamakulov
Copy link
Author

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 :–)

@septatrix
Copy link

Based on my understanding #1349 only applies to <link> tags inside the <body> tag? This issue however also covers links in the head for which there currently is no js-less method for non-blocking rendering to my knowledge. As others have noted the current workarounds are cumbersome and complex, especially to new devs. Even worse is that they all require javascript which not all users might have turned on and the fallback would be that no(!) stylesheets would be activated at all.

@yoavweiss
Copy link
Contributor

We discussed maybe expanding the blocking=render attribute behavior to include blocking=!render or some other indication that a style shouldn't block rendering.

/cc @xiaochengh

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addition/proposal New features or enhancements topic: style
Development

No branches or pull requests

8 participants