tl;dr: We should offer a simple solution for async loading stylesheets at a low-priority. Something in the vein of <link async> or <link importance="low">.
The Filament Group recently posted a pattern for loading CSS asynchronously, leveraging a workaround setting a link's media attribute to print:
Although link[rel=preload] exists for achieving a similar pattern (toggle rel once loaded vs media), it fetches files early at a higher perceived priority. This can have the downside of de-prioritizing other critical requests.
An alternative to the print pattern above would be standardizing on a way to async load stylesheets with a low-priority. This could be achieved in a few ways:
Modify the behavior of Priority Hints such that <link rel="stylesheet" href="/path/to/my.css" importance="low"> both signaled low-network priority and that the link should be non-render blocking
Starting this discussion to explore whether this is overall problem is a pattern we would like to better solve in the web platform and if so, what the shape of a solution should be.
Curious if <link rel="stylesheet" href="/path/to/my.css" defer> is meaningful as well, in case the order of imports can changes which is overwriting which rulesets / styles.
We definitely want defer semantics here, regardless of the markup used. Current JS async semantics are… not good. They are racy by definition. We shouldn’t add that kind of raciness to CSS, unless people explicitly want it (and then they can use preload to recreate it :D).
Yes, async interrupts the parser, while defer waits for the page to finish parsing. External style sheets that are non-critical should probably not interrupt parsing.
That’s a Chrome implementation detail, sounds like. In Gecko it doesn’t do that; it will run if/when the parser is interrupted, but will not trigger an interrupt itself.
Is Firefox’s behavior in accordance with the spec? The spec text suggests that an async script always interrupts the parser.
For classic scripts, if the async attribute is present, then the classic script will be fetched in parallel to parsing and evaluated as soon as it is available (potentially before parsing completes).
How can you tell when the script is “available” except for “when it gets run”? In particular, in Firefox it’s not “available” until a task runs on the main thread notifying it that it’s available, and those tasks don’t interrupt the parser.
That’s as far as observable behavior goes. In spec terms, “available” is not really defined; it’s up to the browser to decide when the script is “available”.
This means the execution of script1.js would be blocked by the fetch & calculation of style1.css. Whereas script2.js would not be blocked by style1.css.