A recent blink-dev intent and ensuing discussion got me thinking about the current ways we load styles.
This was heavily discussed in the past (#1, #2), but those discussions seemed focused on one case, where I think we can benefit from a more holistic discussion.
AFAIU, we have a few different cases for styles loaded today:
Critical CSS - required for the page’s initial rendering. Should block rendering.
Non-critical CSS - needed for the current page’s hidden parts. May or may not need to block the rendering of those hidden parts.
Non-matching CSS - not required for current page unless conditions change. (e.g. print)
(1) is implemented through regular style links today, and currently (at least in Chromium), blocks the rendering, but not the HTML’s parsing. One side effect of that is that async scripts defined after the style (with no blocking scripts in the way) can still execute before the stylesheet finished downloading, enabling interesting loading patterns.
The intent above wants to change that, in order to reduce code complexity, but at the expense of those patterns.
(2) can be implemented in multiple ways. One such way is to have the style links be embedded in the body, right above the content which they impact.
Another is to use script-driven CSS loading patterns.
Those 2 approaches have different characteristics. The former blocks the parser (at least in Chromium), meaning that async scripts defined after it will not run before it finished loading. On the other hand, it also means that it will block rendering of everything below it.
The latter approach doesn’t impact rendering at all, until the style finished loading, so can result in FOUC, but on the other hand, might be faster, as parsing is not paused.
Seems like it would be good to document:
The use-cases for running async scripts while styles are loading
If it’s just for timing out blocking styles, maybe we can come up with a better API
The use-cases for non-critical styles that shouldn’t block rendering at all
AFAICT, that’s the use case for deferred stylesheets beyond the current in-body behavior, but it’s not clear to me what it is…
 Browsers disagree on many things related to style loading. It would be good to try and coalesce behaviors here, but that may be a tangent.
Thanks for starting this discussion. My use of non-blocking styles include:
Content below-the-fold that cannot be seen by the user initially.
This should block if the user scrolls to the content before the stylesheet is loaded however.
Content that is not visible to the user until after an interaction. For example content that is inside of a closed summary/detail. Or content that is for another “page” that will be displayed via SPA routing.
Less common, but occasionally I have divided stylesheets into critical, such as page structure and sizing values (margin, opacity), and less critical such as colors, border radiuses and box shadows.
Supporting deferred styles won’t help avoid the parser blocking behavior, as some styles need to avoid FOUC
What Chromium does today for in-head styles is to block rendering without parsing. That significantly complicates the rendering code.
Is there a third option?
We could provide opt-ins to stylesheets that indicate that their styles are not referred to by following scripts, and therefore their download shouldn’t block those scripts. That wouldn’t solve the complexity issues around continuing to parse without rendering. I’m also not sure that is something developers can easily predict (but maybe tools can help us, if it’s beneficial).