tl;dr: We should offer a simple solution for async loading stylesheets at a low-priority. Something in the vein of
<link async> or
The Filament Group recently posted a pattern for loading CSS asynchronously, leveraging a workaround setting a
media attribute to
<link rel="stylesheet" href="/path/to/my.css" media="print" onload="this.media='all'">
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:
<link rel="stylesheet" href="/path/to/my.css" async>
- 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.
<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.
I like the idea of native async
Would prefer an obvious signal e.g.
async attribute as JS has over
importance=low signalling non-blocking behaviour behaviour
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).
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.
async interrupts the parser
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”.
If we go for
defer semantics (I guess we could have both), would it be in the same queue as
<script defer>? That would be nice…
<link defer rel="stylesheet" href="style1.css">
<script defer src="script1.js"></script>
This means the execution of
script1.js would be blocked by the fetch & calculation of
script2.js would not be blocked by
I don’t want to disrupt this discussion, but FYI there’s the “A standard method for loading style sheets asynchronously” post too.