Short Bytes: Until now, only in IE/Edge could we leverage streaming of CSS to load progressively the CSS of the components as their HTML is streamed. In other browsers, they would normally block rendering till the entire CSS sources were loaded, giving us the white screen of wait before anything could render. With the latest Chrome Canary, Google has picked the CSS streaming idea from IE/Edge and is in the process of an open discussion to start its standardization by working on its specification.Before I start writing what I want to say in this post, here’s the gist of it:
— Jake Archibald (@jaffathecake) February 10, 2017
<head> <link rel="stylesheet" href="/yourstyles.css"> </head> <body> …content… </body>
Right now, CSS blocks rendering leaving the user to a white screen, until yourstyles.css is completely downloaded.
It’s a common practice to bundle our site’s CSS only in one or two files. This might lead to the user downloading the CSS which might not be currently needed for the page he has landed on. Now, you may ask, why is it even a common practice to do so? Because delivering component level (a web page is made of many components, for example, header, chat, navigation bar, footer, etc.) CSS means fetching multiple CSS files (in HTTP/1) which can further elongate that white screen of wait.
However, this isn’t the case in the next gen HTTP/2 protocol, where many small resources can be delivered with a little overhead and can be independently cached.
<head> <link rel="stylesheet" href="/header.css"> <link rel="stylesheet" href="/article.css"> <link rel="stylesheet" href="/chat.css"> <link rel="stylesheet" href="/about-me.css"> <link rel="stylesheet" href="/footer.css"> </head> <body> …content… </body>
HTTP/2 solves multiple fetches slowing down a page download problem. But it means that you need to know what the page will contain when you are outputting the contents of the head tag. Also, the browser will still have to download all the CSS before anything is rendered for the user. That is, a slow loading footer will delay rendering for everything.
There’s a quick hack for it. You could inline your critical CSS so that you can style your major components or containers, and then async-sort-of lazy load the CSS later. But the biggest problem with inlining critical CSS is, that you have to set display: none to many components whose CSS hasn’t been loaded yet. When that CSS resource is loaded, things move here and there like this.
THE FOLLOWING HAS BEEN SHIPPED IN THE CHROME CANARIES NEAR YOU
<link rel="stylesheet"> blocks rendering of subsequent content while the stylesheet loads, but allows the rendering of content before it.
The stylesheets load in parallel, but they apply in series.
<link rel="stylesheet"> behave similar to
<head> </head> <body> <!-- HTTP/2 push this resource, or inline it, whichever's faster --> <link rel="stylesheet" href="/header.css"> <header>…</header> <link rel="stylesheet" href="/article.css"> <main>…</main> <link rel="stylesheet" href="/chat.css"> <section class="comments">…</section> <link rel="stylesheet" href="/about-me.css"> <section class="about-me">…</section> <link rel="stylesheet" href="/footer.css"> <footer>…</footer> </body>
Let’s say the header, article, and footer CSS have loaded, but the rest are still pending, here’s how the page would look:
- Header: rendered
- Article: rendered
- Chat: not rendered, CSS before it(the HTML) hasn’t loaded yet (
- About me: not rendered, CSS before it hasn’t loaded yet (
- Footer: not rendered, CSS before it hasn’t loaded yet (
/chat.css), even though its own CSS has loaded
This helps sequentially render the page. One doesn’t have to decide which parts of the page, what containers and layouts should one inline and then change the display: none to show the incoming CSS changes. It’s fully streaming compatible because one doesn’t need to output the link until just before one needs it. CSS Resources Load Progressively.
But did you see something peculiar? Did you? You didn’t see THE LINK TAGS IN THE BODY? Didn’t it seem strange?
The HTML spec doesn’t cover how CSS should block page rendering, and it discourages
<link rel="stylesheet"> in the body, but all browsers allow it. Of course, they all deal with link-in-body in their own ways:
- Chrome & Safari: Stops rendering as soon as the
<link rel="stylesheet">is discovered, and won’t render until all discovered stylesheets have loaded. This often results in unrendered content above the
<link rel="stylesheet">in the head blocks rendering until all discovered stylesheets have loaded.
<link rel="stylesheet">in the body does not block rendering unless a stylesheet in the head is already blocking rendering. This can result in a flash of unstyled content (FOUC).
- IE/Edge: Blocks the parser until the stylesheet loads, but allows content above the
<link>to render. This is the pattern on the lines of which Chrome Canary has shipped progressive CSS rendering like explained in this article.