#727: Review request on `blocking=render` attribute for scripts and stylesheets

Visit on Github.

Opened Apr 5, 2022

Braw mornin' TAG!

Edit: This feature now affects scripts and stylesheets only. The behavior on preloads have been removed due to whatwg/html#7896.

I'm requesting a TAG review of blocking=render attribute for scripts and stylesheets <strike>and link resources</strike>.

All current browsers already have a render-blocking mechanism: after a navigation, the user agent will not render any pixel to the screen before stylesheets and synchronous scripts are loaded and evaluated (or a UA-defined timeout is reached), to prevent a Flash of Unstyled Content and ensure that critical scripts are evaluated. We extend this idea by introducing the blocking=render attribute, which can explicitly mark other resources (script, style and link of types <strike>modulepreload, preload and</strike> stylesheet) as render-blocking, so that a flash of undesired contents can be prevented in more use cases (see explainer).

Further details:

  • I have reviewed the TAG's Web Platform Design Principles
  • Relevant time constraints or deadlines: April 22, 2022
  • The group where the work on this specification is currently being done: WHATWG
  • Major unresolved issues with or opposition to this specification: None
  • This work is being funded by: Google

We'd prefer the TAG provide feedback as (please delete all but the desired option):

💬 leave review feedback as a comment in this issue and @-notify xiaochengh@

Discussions

Discussed Apr 1, 2022 (See Github)

Sangwhan: the more I think about it the more harmful I feel it is. What this tries to do is introduce a attribute to async loaded subresources to block the render. To prevent unstyled flashing. Which I an understand why they want to do it but I'm not so sure if the gains outweigh the harm. The risk is eg if a resource marked as blocking is incredibly heavy and you're on a slow network. Right now you get some progressive rendering, you can read unstyled text. Another thing is if an ad script is 'blocking', which it shouldn't, but.

Dan: who gets to say what's blocking?

Peter: on the tag

Dan: for instance publishers or websites could be compelled to set it as blocking by advertisers

Sangwhan: advertisers have every incentive to set it to blocking, because it guarantees they will have an impression. If the user quicky navigates away now they won't get an ipression. It would be in ad networks greatest interst to set it as blocking, but taht would be harmful because its a massive pile of js that is not relevant to the content.

Amy: how would that play with ad blockers? Would nothing load?

Sangwhan: the fetch fails if there's an ad blocker. If the fetch fails in their proposal then it's fine, shouldn't block it.

Peter: and they do accept you may still want to time out. Their use cases are weak. One is loading a font. Webkit used to do this with 30 second timeouts on fonts, and users were staring at blank pages while the font loaded. That was bad and you can control it with css, why add something else?

Sangwhan: if you do this with cjk fonts that's going to be fun... they're like 5mb

Peter: they can be. They give a use case of using a script to inject a stylesheet and you want it to be blocking. Seems like you're already kind of past the..

Dan: aren't all of these cases antithetical to the spirit of the web?

Sangwhan: it disobeys our previous stance on making everything async friendly. Suddenly this is adding a big block of sync in an async system. Feels like it would be abused too much.

Peter: example of wanting to block render on an async script load.. seems completely counterintuitive to me.

Sangwhan: if it's just to prevent the flashing of unstyled content, a weak story. The design should be revisited.

Dan: Sounds strong enough to leave feedback along those lines

Sangwhan: one way it could be approached with this design is the site does that but the user is presented with a dialog that indicates preference... seems like too much..

Dan: if the use cases are as weak.. who is asking for this? What's the pressure?

Peter: what user need is this serving? As far as I can tell the need is for developers to work around issues with frameworks

Sangwhan: if you have a massive SPA but a webpage that loads before that.. I guess that.. making a block and not showing the html part maybe makes sense? Still seems weak.

Dan: we should leave feedback

Peter: just wanting to make sure we have broader consensus [will leave comment]

Discussed Apr 1, 2022 (See Github)

Use case for font blocking is replicating controls already available in CSS: https://drafts.csswg.org/css-fonts-4/#font-rendering-controls-introduction

Blocking is bad do we really want to add more mechanisms to do this arbitrarily?

Discuss in plenary

Discussed Apr 1, 2022 (See Github)

left comment channeling Sangwhan's comments

Comment by @LeaVerou Apr 18, 2022 (See Github)

How does the corresponding IDL attribute work? Would element.blocking === "render" be true for elements that are render-blocking by default?

Comment by @xiaochengh Apr 18, 2022 (See Github)

No. element.blocking simply reflects the content attribute.

Comment by @plinss Apr 21, 2022 (See Github)

@cynthia and I took a look at this this week and we have some concerns about what user need this is actually serving?

The explainer list a uses case of wanting to block on font loading, which can already be controlled via CSS, is generally considered a bad thing to do by the CSSWG, and usually leads to a very bad user experience. The other use cases seem somewhat questionable.

This feature also goes against one of the TAG's design principles. Is there really a need to add more render blocking?

Comment by @xiaochengh Apr 21, 2022 (See Github)

Thanks for the review! My response is as follows:

The explainer list a uses case of wanting to block on font loading, which can already be controlled via CSS

CSS font-display descriptor cannot control render-blocking. It only leaves an empty space for the text that uses the web font, and still renders other contents. Since the empty space is measured using a fallback font, its dimensions can still change when the actual web font is swapped in, causing a layout shift. There is a rich literature about this, to name a few:

The other use cases seem somewhat questionable.

I believe the other use cases are also as strong.

For the second use case (script-inserted style & script): In practice many web developers struggle to properly pre-process their blocking style sheets and scripts, which is one reason CLS is quite common. This is especially true for individual, educational or indie developers, who don't have the resources to build & maintain fancy server-side templating and serving systems. The linked educational example is one such example, which is served from raw sources without any build step, has many constituent pages, and a loader script to avoid duplication of common resources (& corresponding mistakes) across these pages. There is no advantage at all to the multi-render progressive loading for this site, it just looks jarring and ugly. blocking=render solves it elegantly.

For the third use case (async script), blocking=render provides immediate improvements to client-side A/B testing. We need to ensure that the test config script (which may, e.g., restyle certain elements) is run before rendering starts. Otherwise, the user may interact with an unconfigured page, making any subsequent measurements invalid. Current approaches either manually hide the document with JS, or do nothing and risk having invalid results. Also @alexnj to elaborate.

This feature also goes against one of the TAG's design principles.

The principle allows adding such features “only in cases when the overall user experience is improved”, which I believe is exactly the case for blocking=render. In all the use cases, blocking=render prevents a flash of unstyled content, which the principles already deemed “undesirable”.

Comment by @plinss Apr 26, 2022 (See Github)

Bumping this issue until Sangwhan is available, IIRC some of his concerns were about how this feature can be abused (either intentionally or unintentionally) causing significant delays for users on slow connections or who need to download very large resources (like CKJ fonts) that the page author may not take into consideration.

Comment by @xiaochengh Apr 29, 2022 (See Github)

Regarding the concern:

  1. Since there are scenarios where this feature seems necessary, but also other scenarios where misusing the feature leads to a bad performance, we recommend web authors to use it with caution and only when necessary. It's also no more (or less) of a rendering footgun than putting a script or stylesheet in the head. If that resource has a problem then you have a SPOF but it's not particularly extraordinary in the risk.

  2. The risk of blocking=render might also be smaller than traditional scripts and stylesheets. Web authors can, e.g., set a timeout that removes the attribute, to manually unblock rendering if it takes too long to load. This is not achievable for traditional scripts and stylesheets.

Comment by @xiaochengh May 17, 2022 (See Github)

Hi TAG,

Due to whatwg/html#7896, we have removed blocking=render on preload. As a result, now it only affects scripts and stylesheets, which is a small extension since these elements can already be implicitly render-blocking.

Comment by @yoavweiss May 25, 2022 (See Github)

Friendly ping, as this now has a pending intent :)

Discussed Aug 1, 2022 (See Github)

Peter: they removed a large part of the functionality... now this only effects scripts and stylesheets... So curious as those are generally blocking things what does this do?

... they've added the ability to block rendering... doesn't block parsing but does run before rendering starts... I'm kind of OK with that.... opting in to async processing... don't understand what this does with stylesheets.

Dan: No signals on multi-stakeholder support. Does that matter?

Rossen: have we discussed having the multi-stakeholder thing automated ... so it's part of the form?... standing issue...

Tess: I like that idea...

Rossen: should be part of the template of the form.

Tess: multiple, interoperable implementations ++

Dan: I'll take a look at what we can do in the issue template

Peter: back to issue their use case still seems to be delibately blocking ... could be bad on a slow connection. Also numerous issues in CSS wg ... related to web fonts. We fixed that in CSS... I'm happy to let this proceed for script but not happy with it on style.

Rossen: +1

Peter: will leave feedback accordingly

Comment by @plinss Aug 29, 2022 (See Github)

We're OK with adding this to script tags as that usage can be interpreted as adding the ability to add async processing to already blocking scripts, so this is an enhancement.

However, we still have concerns about adding additional blocking to stylesheets, particularly for the purpose of blocking on font loading (and as @cynthia mentioned, CKJ fonts can be large and connections can be slow). In the past webkit blocked for up to 30 seconds on webfont loading and the user experience was horrible. CSS added mechanisms to control rendering with webfonts, adding an additional mechanism to achieve this seems bad.

Comment by @xiaochengh Aug 30, 2022 (See Github)

Thanks for the review!

However, we still have concerns about adding additional blocking to stylesheets, particularly for the purpose of blocking on font loading

Adding this to stylesheets will not block page rendering on web font loading. It will only block on critical subresources, which is currently underdefined, but so far no implementation includes web fonts as critical subresources.

We are indeed pursuing CSS-based approaches to for rendering control with web fonts, which however should be orthogonal to this blocking=render feature.

Discussed Feb 1, 2023 (See Github)

Peter: sangwhan had concerns about handling of CJK fonts... blocking would not be good. CSS already has controls over web fonts loading.. so I was pushing "why this"....

... response doesn't make a lot of sense to me ...

Lea: I don't understand why they say "We are indeed pursuing CSS-based approaches to for rendering control with web fonts" - I think these solutions are out there and working for a while now.

Peter: agree. They do link to a CSS issue. Maybe looking to tweak it...

Dan: looks like this has been stuck since August...

Lea: we should look at the CSS working group thread...

Peter: it was discussed in CSS wg...

Peter: my understanding... won't block on "critical subresources" - my understanding is a style tag with an inline style sheet then that becomes blocking...

Lea: I believe so. Do they want to block for subresources as well?

Peter: only "critical" - only other import stylesheets. Doesn't sound like it's a change in behaviour.

Lea: seems like this is already happening...?

Peter: mainly the ability to add the blocking to the link tag which is currently not blocking.

...

Lea: looking at the explainer... "The user agent will not render any pixels ..." They are adding blocking=render to an async script...

Peter: Sangwhan amd I discussed the script use case and we were OK with it.

... remaining concerns were about style sheets... but if they are already blocking in all cases then ... ?

Lea: clearly it's not because people involved in this are not new to this whole thing.

Lea: suggest: "As currently written your explainer doesn't answer the question... "

Peter: in explainer they're talking about a script tag that injects a link tag and they want that to be blocking.

... so it's mostly about when tags get injected later.

Dan: if it's about when tags get injected that sounds useful. If it's mimicking existing behaviour but covering that case, can we close and say it looks good?

Peter: slightly concerned that adding more mechanisms to block rendering will increase time to first paint.

Lea: it's in the developer's interest to have fast loading pages... But there are now multiple subtly different mechanisms for blocking different things and that's difficult for developers.

Peter: use case of letting script inject style tags - will be used for feature detection e.g. - i would argue that should be handled by pure CSS. So this seems like doing CSS in JS in some cases...

Dan: can we raise these points - developer complexity and duplicating a functionality that CSS ought to be doing anyway - as part of our review? Satisfied with concerns?

Peter: that works for me.

Lea: yeah.

Peter: after the meeting I'll close it.

Comment by @plinss Feb 20, 2023 (See Github)

Thanks for the response, we're going to close this review as satisfied, but we still have some concerns. Mainly, new mechanisms to block rendering need to be used very carefully by developers or they can easily fall into traps that result in a worse user experience (e.g. not realizing how slow user's devices and connections may be in the wild), so developers need to tread very carefully when using this and the additional complexity needs to be carefully explained.

Furthermore, some of the use cases seem to be replicating existing capabilities that would be better handled declaratively. For example, using script to inject links to stylesheets in certain situations should rather be handled by CSS mechanisms like feature and media queries so the UA can scan for preloading opportunities, handling is not gated by script execution, etc.

We do like the possible future extensions of being able to relax blocking in situation that are currently blocking.