You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Allow css files to be marked as async meaning that they will not block rendering. The syntax could be a new attribute on the link element or a new value for "rel" attribute (that already have link types such as "dns-prefetch", "preconnect", "prefetch", and "preload" that also seems to serve a technical, how-to-load purpose), say "<link rel='stylesheet async' type='text/css' href='theme.css'>".
This functionality is similar to the async attribute on the script element, and the font-display descriptor for the @font-face CSS at-rule.
A web search will find quite a few articles about how to do this in a more or less hackish way, so there seems to be a demand for it. It seems that the browsers could rather easily add this in a much more reliable way as the browser e.g. knows if the resource is already in the cache and can control the priority of requests.
(I did read the contributing guidelines, but as I read them, they only talk about submitting pull requests and not about submitting issues)
While the JS-based packaging does not have an issue with loading CSS on demand of JS module( lazy or in run time), IMO having coherent phased load behavior over all resources including script, images, fonts, css, etc has sense to be unified and be a part of HTML standard. So I would extend this feature request to "unify the load behavior for web page resources" instead of just CSS lazy load.
Also current separation of synchronous/async/delayed behavior does not reflect the need for modern web app. In complex apps the order of load is phased and each phase accounts dependency graph. Extending loading parameters with "depend on" and "load order" is a good addition to proposal.
All seems to be easy polyfilled for backward compatibility.
What should the browser do before a resource is downloaded? For fonts this might be wait for the font to download before showing anything, show invisible text, or show a fallback font some of which can be selected by font-display (note it does not seem to be possible using font-display to opt-out of the initial wait), for scripts it might be to wait for the script to load before proceeding or just to execute it when it is loaded, for images there is "lowsrc" and one might imagine an option to select how the image should animate on to the image when it is loaded. In short, it depends on the resource type what possibilities it makes sense to choose between.
What priority should the resource have in the load order? This could tie in with the priority and dependency system that both http2 (section 5.3) and quic (section 3.2) seems to already have. It would probably make sense to give page authors a way to indicate a desired load-order instead of relying on the browsers heuristic.
As an example of why they are orthogonal, consider that it currently seems, that marking a script "async" will give it the lowest priority. But even though a script might not be needed for the first render, it might be desirable to have it load before, say, large decorative images on the page.
This issue is about (1) for the stylesheet resource type.
Reacted by Zach Leatherman, Sérgio Fontes, Milan Raj, James Nash, Ryan McNeely, Shawn Beatty, Lucas Kulnig, Sasha Firsov, Michael Schmidt and 𝕍𝕖𝕝𝕠𝕔𝕚𝕗𝕪𝕖𝕣
I still frequently find reasons for loading CSS asynchronously in my work. As @LeaVerou mentioned, the "print media hack" is commonly referenced as a workaround, but its reliance on JavaScript to apply CSS is a major downside.
The addition of an async attribute for the link element would allow developers to load CSS in a non-render blocking manner without relying on JavaScript. By default, I think this should cause a stylesheet to load at a low fetch priority, but the already standard fetch-priority attribute gives us control to change that when desired.
I'd love to volunteer my time to consult on specification or implementation work if that's helpful in any way. Thank you!
Reacted by Ben Schwarz, Matt Henry, Casey Carroll, Kainoa Kanter, Ryan Townsend, Jasper Moelker, Christopher Kirk-Nielsen, Adam Argyle, Zach Passen, Juan Ferreras and 98 more
Reacted by Randolf Richardson 張文道, Luke Hatfield and heudd
The addition of an async attribute for the link element would allow developers to load CSS in a non-render blocking manner without relying on JavaScript.
CSP settings avoiding inline JS also complicate this common workaround. Another win for a native implementation.
Reacted by Scott Jehl, Ryan McNeely, DjWebdroid and Lucas Kulnig
I'm happy to submit a proposal for blocking=none as per #3983 (comment), but one q:
Why has putting those link tags at the beginning body not become a practice for this? It seems simple enough and potentially solves the problem. Is that a matter of popularizing it via DevRel/education? Or is it a material issue with this alternative?
@noamr I like how that sounds but I think async feels more intuitive.
As for the question of in-body links, my understanding is they are sync and block rendering for HTML that follows them. Useful for different contexts but not the same. [update: browser behavior varies a lot here!]
@noamr I like how that sounds but I think an attribute that's already in use would be much more intuitive (async).
blocking is also already in use, you use if to put blocking=render on scripts, or on dynamically inserted link elements.
But currently it can only be used to add render-blocking and not remove it.
As for the question of in-body links, my understanding is they are sync and block rendering for HTML that follows them. Useful for different contexts but not the same.
That's incorrect AFAICT. <link> elements inside the body are always async as per spec and don't have the script-like semantics of blocking parsing.
We should be clear here with the use of the word sync - fetching of external resources can either block parsing or block rendering.
Only classic scripts block parsing.
Only elements in the head can block rendering (script, link).
Yea I believe this is not per spec. It's a browser heuristic, not sure I like it :)
Thanks for the clarification @bramus, I got this wrong indeed and was looking too much at the spec.
Anyway, putting the <link> after the elements that don't rely on it and before the ones who do, with a corresponding preload if you must, sounds like an OK-ish practice?
I'm afraid that <link rel=stylesheet blocking=none> (or async or what not) would lead people in the direction of creating hard to detect FoUCs.
Having spoken with @scottjehl offline, I recognize that a valid use case for this is when the elements being styled asynchronously have some kind of default CSS that makes them hidden until the CSS is loaded, which would make them "styled into being visible". This is a legitimate use case because the resulting UX is not a FoUC.
I wonder if the web platform should enable this use case directly, or if that's enough to justify the simpler-but-footgunny "async" attribute.
<!-- This would load (like preload), but would only apply & block parsing when an element with ID="some-id" is seen --><!-- If the target element is something like an async script, we can keep parsing but block the script execution on the stylesheet --><linkrel=stylesheethref="widget.css" for="some-id">
How do you intend this to work when the <foo id="some-id"> comes from document.write? (Previously, we've thought it bad for the point at which document.write returns to depend on whether an external script is already in the cache or not.)
For markup coming from the network, it does not seems as problematic to allow arbitrary elements to block the parser, though it doesn't seem particularly great for performance to have to perform a "does the parser need to be blocked on ID match" on every element", but perhaps it can be optimized well enough by making documents carry a flag indicating whether this feature is in use at all, which would provide for a quick check for all the pages that don't use the proposed feature.
A comment that expands on many of the use cases in this issue, as well as why the existing "print media" workaround already does exactly what we need, and only has a few drawbacks that a standard async attribute would address:
putting the sheet at the "end of the body" (a) already works (in Chromium and mostly Gecko so far) and (b) hopefully makes them think more about the implications in a more intuitive way.
It may often work, but there's a problem with moving stylesheets to the footer: it can break the expected CSS cascade. I haven't seen this discussed so far. I'm working on improving performance in WordPress core (e.g. WordPress/performance#2076 (comment)), and one area I've been looking at is deprioritizing styles for blocks that aren't in any initial viewport. For example, there is a "Social Links" block and its stylesheet is quite large, so having it as a blocking stylesheet in the head is problematic when the block only appears in the page footer. Moving it to the end of the body fixes this issue, but it causes the order of CSS rules to change.
In WordPress, as surely in many CMSes, a site owner can add their own CSS overrides which appear at after other stylesheets have been printed in the head. So they may something like:
This will have the desired style applied when the Social Links stylesheet is printed previously in the head, but this user CSS rule gets unexpectedly overridden when the stylesheet is moved to the body by this core CSS rule:
This makes it unreliable to move a stylesheet from the default head placement to the end of the body, even though a page analysis may indicate this is better for performance since the bulk of the CSS is not needed until the footer is scrolled into view. It means optimizations cannot be applied automatically without manual testing to ensure the cascade of CSS rules doesn't break. The print media hack does not have this problem since the order of the cascade is preserved (h/t @eric-michel).
@nyaxt and I discussed a related topic today, which is:
The Chromium implementation has stylesheets block the parser
The spec has stylesheets block sync scripts. (And sync scripts block the parser.)
So in most real-world cases, where you have code like <link rel="stylesheet"><script src="non-async-non-defer-non-module.js"></script> somewhere at the top of your page, the spec's behavior will be about as blocking as Chromium's.
But it's possible to construct pages where the spec blocks less than Chromium, e.g. a page with no blocking scripts, or a page where all the blocking scripts are before the <link rel="stylesheet">s.
What web developers seem to care about, and what everyone in this issue is talking about, seems to be render-blocking.
For the difference between Chromium and the spec, see #1349.
The difference between blocking scripts and blocking rendering is that if you only block rendering, scripts might execute and see incorrect values from getComputedStyle() or similar. The spec discusses this in the note at https://html.spec.whatwg.org/#interactions-of-styling-and-scripting .
However, I feel like the spec's current choice is a very conservative default. It might be worthwhile letting pages opt out certain stylesheets from script-blocking, while still keeping them as render-blocking.
What web developers seem to care about, and what everyone in this issue is talking about, seems to be render-blocking.
Since I’ve been involved in the discussion, I can say that render blocking is the aspect I care most to opt-out of (and I think most folks are looking for the same when they mention async css). Opting out of script blocking as well seems expected, but the former is the bottleneck something like the print-media workaround seeks to avoid.
Theproposal of loading CSS lazily would not be complete without
Freeze css for DOM sub-tree.
meaning any runtime changes to css via load or otherwise would not be applied to particular section of DOM. In addition to performance gain( I hope there is no need to explain ), it would give assurance that 3rd-party css loaded later would not interfere.
It sounds quite similar to shadow root insulation except of scope defined by the load time.
The moment of freeze is when all dependencies resolved.
I would suggest one of these attribtues, in order of preference (rather than a new rel, as that will break compatability)
async (copied from <script>)
blocking=false (reuses existing attribtue)
blcoking=never (reuses existing attirbute)
(yes I do know that I'm breaking spec by using <ul> for a list with meaningful order; I just didn't want for there to be numbers and github blocks CSS in issue comment)
Activity
sashafirsov commentedon Aug 31, 2018
While the JS-based packaging does not have an issue with loading CSS on demand of JS module( lazy or in run time), IMO having coherent phased load behavior over all resources including script, images, fonts, css, etc has sense to be unified and be a part of HTML standard. So I would extend this feature request to "unify the load behavior for web page resources" instead of just CSS lazy load.
Also current separation of synchronous/async/delayed behavior does not reflect the need for modern web app. In complex apps the order of load is phased and each phase accounts dependency graph. Extending loading parameters with "depend on" and "load order" is a good addition to proposal.
All seems to be easy polyfilled for backward compatibility.
andershol commentedon Sep 6, 2018
There are two related but orthogonal issues here:
As an example of why they are orthogonal, consider that it currently seems, that marking a script "async" will give it the lowest priority. But even though a script might not be needed for the first render, it might be desirable to have it load before, say, large decorative images on the page.
This issue is about (1) for the stylesheet resource type.
noamr commentedon Aug 1, 2024
Perhaps we can add a semantic that
<link rel=stylesheet blocking="none">would make parser-inserted scripts not render-blocking by defaultLeaVerou commentedon Aug 1, 2024
Maybe we can revive this issue?
Async CSS is pretty essential. It's the reason libraries like Font Awesome have to resort to
<script>for their embed code.Tons of questions online about CSS async loading, all resorting to JS in one way or another, with the prevailing solution being this:
scottjehl commentedon Jan 9, 2025
I still frequently find reasons for loading CSS asynchronously in my work. As @LeaVerou mentioned, the "print media hack" is commonly referenced as a workaround, but its reliance on JavaScript to apply CSS is a major downside.
The addition of an
asyncattribute for the link element would allow developers to load CSS in a non-render blocking manner without relying on JavaScript. By default, I think this should cause a stylesheet to load at a low fetch priority, but the already standard fetch-priority attribute gives us control to change that when desired.I'd love to volunteer my time to consult on specification or implementation work if that's helpful in any way. Thank you!
rajsite commentedon Jan 9, 2025
CSP settings avoiding inline JS also complicate this common workaround. Another win for a native implementation.
noamr commentedon Jan 9, 2025
I'm happy to submit a proposal for
blocking=noneas per #3983 (comment), but one q:Why has putting those
linktags at the beginning body not become a practice for this? It seems simple enough and potentially solves the problem. Is that a matter of popularizing it via DevRel/education? Or is it a material issue with this alternative?scottjehl commentedon Jan 10, 2025
@noamr I like how that sounds but I think
asyncfeels more intuitive.As for the question of in-body links, my understanding is they are sync and block rendering for HTML that follows them. Useful for different contexts but not the same. [update: browser behavior varies a lot here!]
noamr commentedon Jan 10, 2025
blockingis also already in use, you use if to putblocking=renderon scripts, or on dynamically insertedlinkelements.But currently it can only be used to add render-blocking and not remove it.
That's incorrect AFAICT.
<link>elements inside the body are always async as per spec and don't have the script-like semantics of blocking parsing.We should be clear here with the use of the word
sync- fetching of external resources can either block parsing or block rendering.script,link).So the following should just work:
bramus commentedon Jan 10, 2025
These too are blocking. See https://codepen.io/bramus/pen/NPKYWrX/23a03a80e29b92fa80a41701959d1dba for a demo.
UPDATE: In Chrome and Safari. Not in Firefox.
noamr commentedon Jan 10, 2025
Yea I believe this is not per spec. It's a browser heuristic, not sure I like it :)
Thanks for the clarification @bramus, I got this wrong indeed and was looking too much at the spec.
rik commentedon Jan 10, 2025
Yeah, browsers all behave differently. 8 years ago, Jake wrote about it.
noamr commentedon Jan 10, 2025
Anyway, putting the
<link>after the elements that don't rely on it and before the ones who do, with a corresponding preload if you must, sounds like an OK-ish practice?I'm afraid that
<link rel=stylesheet blocking=none>(orasyncor what not) would lead people in the direction of creating hard to detect FoUCs.bramus commentedon Jan 10, 2025
It was also covered by @csswizardry in https://csswizardry.com/2018/11/css-and-network-performance/#place-link-relstylesheet--in-body
43 remaining items
noamr commentedon Jan 16, 2025
Having spoken with @scottjehl offline, I recognize that a valid use case for this is when the elements being styled asynchronously have some kind of default CSS that makes them hidden until the CSS is loaded, which would make them "styled into being visible". This is a legitimate use case because the resulting UX is not a FoUC.
I wonder if the web platform should enable this use case directly, or if that's enough to justify the simpler-but-footgunny "async" attribute.
hsivonen commentedon Jan 17, 2025
How do you intend this to work when the
<foo id="some-id">comes fromdocument.write? (Previously, we've thought it bad for the point at whichdocument.writereturns to depend on whether an external script is already in the cache or not.)For markup coming from the network, it does not seems as problematic to allow arbitrary elements to block the parser, though it doesn't seem particularly great for performance to have to perform a "does the parser need to be blocked on ID match" on every element", but perhaps it can be optimized well enough by making documents carry a flag indicating whether this feature is in use at all, which would provide for a quick check for all the pages that don't use the proposed feature.
itsdouges commentedon Jan 18, 2025
Lazy loading themes would be useful.
noamr commentedon Jan 18, 2025
Sure! How would you envision this working when the user changes theme before the theme's css is loaded?
noamr commentedon Jan 27, 2025
As per the WHATNOT discussion, created a WICG proposal: WICG/proposals#195
scottjehl commentedon Jan 29, 2025
A comment that expands on many of the use cases in this issue, as well as why the existing "print media" workaround already does exactly what we need, and only has a few drawbacks that a standard
asyncattribute would address:WICG/proposals#195 (comment)
westonruter commentedon Jul 9, 2025
It may often work, but there's a problem with moving stylesheets to the footer: it can break the expected CSS cascade. I haven't seen this discussed so far. I'm working on improving performance in WordPress core (e.g. WordPress/performance#2076 (comment)), and one area I've been looking at is deprioritizing styles for blocks that aren't in any initial viewport. For example, there is a "Social Links" block and its stylesheet is quite large, so having it as a blocking stylesheet in the
headis problematic when the block only appears in the page footer. Moving it to the end of thebodyfixes this issue, but it causes the order of CSS rules to change.In WordPress, as surely in many CMSes, a site owner can add their own CSS overrides which appear at after other stylesheets have been printed in the
head. So they may something like:This will have the desired style applied when the Social Links stylesheet is printed previously in the
head, but this user CSS rule gets unexpectedly overridden when the stylesheet is moved to thebodyby this core CSS rule:bodyThis makes it unreliable to move a stylesheet from the default
headplacement to the end of thebody, even though a page analysis may indicate this is better for performance since the bulk of the CSS is not needed until the footer is scrolled into view. It means optimizations cannot be applied automatically without manual testing to ensure the cascade of CSS rules doesn't break. The print media hack does not have this problem since the order of the cascade is preserved (h/t @eric-michel).noamr commentedon Jul 10, 2025
Note that I'm working on a solution for out-of-order HTML streaming that could solve this problem without introducing a style-specific solution. See https://github.com/WICG/declarative-partial-updates
domenic commentedon Aug 21, 2025
@nyaxt and I discussed a related topic today, which is:
<link rel="stylesheet"><script src="non-async-non-defer-non-module.js"></script>somewhere at the top of your page, the spec's behavior will be about as blocking as Chromium's.<link rel="stylesheet">s.For the difference between Chromium and the spec, see #1349.
The difference between blocking scripts and blocking rendering is that if you only block rendering, scripts might execute and see incorrect values from
getComputedStyle()or similar. The spec discusses this in the note at https://html.spec.whatwg.org/#interactions-of-styling-and-scripting .However, I feel like the spec's current choice is a very conservative default. It might be worthwhile letting pages opt out certain stylesheets from script-blocking, while still keeping them as render-blocking.
scottjehl commentedon Aug 21, 2025
Since I’ve been involved in the discussion, I can say that render blocking is the aspect I care most to opt-out of (and I think most folks are looking for the same when they mention async css). Opting out of script blocking as well seems expected, but the former is the bottleneck something like the print-media workaround seeks to avoid.
sashafirsov commentedon Aug 26, 2025
Theproposal of loading CSS lazily would not be complete without
Freeze css for DOM sub-tree.
meaning any runtime changes to css via load or otherwise would not be applied to particular section of DOM. In addition to performance gain( I hope there is no need to explain ), it would give assurance that 3rd-party css loaded later would not interfere.
It sounds quite similar to shadow root insulation except of scope defined by the load time.
The moment of freeze is when all dependencies resolved.
Velocifyer commentedon Oct 11, 2026
I would suggest one of these attribtues, in order of preference (rather than a new
rel, as that will break compatability)async(copied from <script>)blocking=false(reuses existing attribtue)blcoking=never(reuses existing attirbute)(yes I do know that I'm breaking spec by using <ul> for a list with meaningful order; I just didn't want for there to be numbers and github blocks CSS in issue comment)