-
Notifications
You must be signed in to change notification settings - Fork 639
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[css-fonts] Add a font-display
keyword to eliminate @font-face
FOIT & layout shifts
#7271
Comments
I'm wondering exactly what it means for a web font to be "truly critical", such that it's reasonable for them to block rendering indefinitely; and if a web font is so critical to the site, why would they be loading it from a third-party server (which might be down, inaccessible, blocked, hacked, etc. at any given time)? |
Examples of truly critical webfonts:
|
Could such cases be handled by directly embedding the font as a data URL within the stylesheet? |
Yes that’s one way, and you could embed the stylesheet in the document of course. In normal practice AFAIK, data URL font stylesheets are typically separate from the main CSS files, so then you might need to defend against the main CSS loading but the font CSS not… |
I don't think this is a new issue. The web font is usually served from the same (or related) server as the font stylesheet, which is also render-blocking and third party.
Data url doesn't guarantee no FOIT / layout shift. For example, in Blink, data urls are handled the same was as all other urls:
|
The CSS Working Group just discussed The full IRC log of that discussion<dbaron> Topic: [css-fonts] Add a font-display keyword to eliminate @font-face FOIT & layout shifts<Rossen_> s/Resolution:/RESOLVED:/ <dbaron> github: https://github.com//issues/7271 <dbaron> xiaochengh: I propose adding a new keyword to font-display descriptior so it's critical and blocks load event of stylesheet. <dbaron> xiaochengh: The purpose is to eliminate flash of invisible/unstyled text or layout shift caused by web fonts. <dbaron> xiaochengh: There are many ways to mitigate flash or layout shift, but either complicated to use or have other issues. <dbaron> xiaochengh: This one keyword proposal can hopefully solve this. <dbaron> Rossen: I see a conversation back and forth between Jonathan and Lawrence on the issue. Jonathan is raising some points -- not sure they've been answered, especially his first point about what is truly critical. <chrishtr> q+ <dbaron> s/Lawrence/Laurence/ <dbaron> s/descriptior/descriptor/ <Rossen_> q? <Rossen_> ack chrishtr <dbaron> chrishtr: regarding critical -- there are examples in issue of font use cases that would be considered critical. This isn't a question about why they'd be served from a 3d party domain if they were critical. If you want your site to be fast you'd ideally serve from same domain, but many sites use 3d party f ont service to serve fonts, which allows 3d party font service to update fonts over time. <dbaron> chrishtr: xiaochengh is proposing that critical fonts should be loaded only if they're truly critical. Hope that font providers could have query parameter so that their stylesheet could say it's render blocking. <dbaron> Rossen_: Jonathan...? <dbaron> jfkthame: Don't have anything specific to add... was trying to understand goals and how it would work. I have a little reluctance to complicate font-display ... already fairly complex that few people understand. But I haven't tried to think through implementation issues. <dbaron> jfkthame: ... of the new critical value. <dbaron> jfkthame: Other question is how this relates to font loading api. Are these use cases where authors should be using font loading api to more closely cnotrol what is happening? Not sure... <Rossen_> ack fantasai <xiaochengh> q+ <astearns> q+ <dbaron> fantasai: My concern is that it prevents the page from showing text. I understand thatintent is that authors not use it for most of the text, but I think authors might use it for that. We normally don't make it easier to prevent the page from loading for long periods of time, perhaps forever. <Rossen_> ack xiaochengh <dbaron> xiaochengh: problem is developers are already trying to block render of page with various hacks. One example is to add empty external stylesheet that blocks rendering so font can load. Other is add display:none or visibility:hidden and remove when font is loaded. Since developers are already trying to do it, we can provide a better way. <chrishtr> also, style sheets and scripts already block rendering, potentially indefintely. <chrishtr> this attribute also indicates intent directly, and allows the UA to ignore it after a timeout <dbaron> fantasai: I think technique of using visibility:Hidden and then visible is the developer very clearly saying that it's not to be rendered until font loads. Very clear they want invisible. <emilio> q+ <dbaron> xiaochengh: not just making element invisible... making entire page invisible. <dbaron> Rossen_: This is use-case specific. People can do it in a more targeted way. <Rossen_> ack astearns <plinss> q+ <fantasai> fantasai: my concern is that someone will be like "Oh, my wedding invitation *has* to be loaded in this font because it looks ugly otherwise, so I definitely consider this a critical font" and then the reader of the invitation, on a different connection, ends up never able to read the page <dbaron> astearns: I think I agree with fantasai that the current hacks might be sufficient for this. But I got on queue because of concern with how this works only with style-blocking style sheets. I worry about adding something that will work in some cases but not in others. We'd have to specify what this does, if anything, if the style sheet is not render blocking. And I'm worried about adding something that has that works and doesn't work <dbaron> astearns: characteristic. <dbaron> xiaochengh: The intention is to make it render-blocking but after the document has already started there's no way to block render, so to keep things simple I'm just making it block load of style sheet. <astearns> s/style-blocking/render-blocking/ <Rossen_> ack emilio <dbaron> emilio: Similar to that... this would imply the font should load unconditionally and fully -- don't have unicode-range (presumably ignored) -- my other question isn't this more similar to how background images block the load event of the page but not of the style sheet. Doesn't achieve the rendering blocking that you want... but maybe it does? Background image loads get started before layout rather than during layout like fonts. <chrishtr> q+ <Rossen_> ack plinss <dbaron> Peter: I agree with fantasai. I hear the argument that authors do this so we should make it easy. That argument falls down when authors are doing something bad -- we have no obligation to do that. We should teach authors how to do fallbacks, progressive rendering ,etc. <fantasai> +1 to "don't optimize for making the bad things easy" <dbaron> chrishtr: Peter, I don't think there is a good way to do it right now. Only way to do it causes flashing on load. size-adjust was added but ??. This is a clean solution to this natural problem. <dbaron> Peter: We have to weight harms between flashing and blank content. Look for other alternative rather than blocking? <dbaron> Rossen: Not sure how this isn't creating flashing as well while you're waiting. <dbaron> chrishtr: Shows white.. shouldn't consider that a flash of unstyled content. <dbaron> fantasai: We already have the block keyword that shows white. <Rossen_> q <Rossen_> ack chrishtr <dbaron> chrishtr: The existing keyword shows white only for the text, not the page <dbaron> ?: ... and there's layout shift. <dbaron> Peter: I'd say both preferable to blank page. <dbaron> chrishtr: I think use cases for either. <dbaron> plinss: Authors don't always consider all the factors <dbaron> s/Peter/plinss/g <dbaron> plinss: Doesn't mean we should make it easier... think it will lead to more abuse. <dbaron> Rossen_: I'm hearing a good bit of feedback. So I think we should take this back to the issue and accumulate a little more consensus there before we bring it back for a resolution. Also given how many people missing today. <fantasai> If we're adding a keyword for this, it needs to be something really obnoxious and obvious, like "block-entire-page-load-forever" <dbaron> Rossen_: Anything else you wanted to highlight today, xiaochengh? <dbaron> xiaochengh: The intention is not to help authors do bad things... let me outline this another way. We're trying to help authors make a tradeoff more easily... tradeoff between page stability and responsiveness. No easy way to go to one end of the tradeoff. <dbaron> Rossen_: We'd like to move it back to the issues. <astearns> I don’t think this is necessarily a bad thing, but I am not convinced there is no current way to achieve an appropriate result <dbaron> fantasai: My understanding is that the proposal is to add a keyword that blocks the page rendering *literally forever* if the font doesn't load. If it's still not loading 30 seconds later because the font isn't loading, that's a problem. <fantasai> s/problem/problem for the user/ <dbaron> plinss: I think the concern is that authors will unknowingly use this badly and wind up doing bad things by accident. <tantek> +1 fantasai, plinss this makes it too easy for authors to do *harmful* things to users <florian> +1 to not linking this, for the reasons said by fantasai and plinss <emilio> one could make a case that the same can effectively already happen for any stylesheet tho <chrishtr> Fantasai: this is not new, style sheets can and do already block rendering. I do think the spec for this should say the UA should provide a timeout. <florian> s/linking/liking/ |
On the question of indefinite blocking in the minutes above, I was interpreting this
to mean that the |
The This (setting a font display timeline) is just for technical completeness. For example, if we have a |
It's correct that this implies the font should load unconditionally and fully. It doesn't mean that The behavior that background images block the load event of the page but not the style sheet seem to be an unspecified behavior. We can't rely on that. Edit: As an example, Google Fonts accepts a https://fonts.googleapis.com/css2?family=Noto+Sans+SC&display=swap&text=%E4%B8%80%E4%BA%8C%E4%B8%89 |
Let me summarize and respond to some major concerns raised in the meeting. The biggest concern is that since render-blocking may cause the page rendering to be blocked infinitely (which is worse than anything else), we shouldn't add new render-blocking things. This is the same general concern I got when specifying "render-blocking" in the HTML spec. To resolve that, UA is allowed to unblock rendering if an internal timeout is reached, even if there are still pending render-blocking resources (scripts & style sheets). See https://html.spec.whatwg.org/C/#render-blocked So this proposal won't make the page never rendered. Nor do other render-blocking resources. Another point is we shouldn't make it too easy for developers to do potentially bad things (e.g., page never renders). If developers are doing workarounds/hacks to achieve that (like adding/removing There might also be concerns that putting things in the declarative syntax is the UA officially saying that this use case is legit and ok to do, whereas the JS hacks don't admit that. First of all, making fonts render-blocking is not an inherently bad pattern, but an alternative FCP vs. CLS tradeoff that developers want to achieve. Forcing a complicated solution is a double-edged sword that, while preventing misuse of this feature, may also lead to other suboptimal workarounds and confusions. Besides, existing JS-based workarounds (like removing Since developers already have a strong need for this, if we ignore it and pretend it's not happening, it eventually comes at the expense of the user because the UA can't do anything about something it doesn't admit. I think it's still better if we can make legit use cases easy to achieve while having a safety net (the UA-defined timeout) against misuses (and bad network), than simply banning it. |
We could also strengthen the language around timeout. e.g. "the user agent should use a more strict timeout for render-blocking fonts, as the user impact of font fallback is less than a completely unstyled document". |
Could you elaborate on why this need is “strong”? I am not sure I have seen evidence for this assertion yet. |
To clarify, "this" means using web fonts without causing layout shifts. It should be quite evident that developers want to achieve it but there's no easy way yet: https://www.google.com/search?q=web+font+cls |
OK, thanks for the clarification. I think things are a little fuzzier about whether developers want to block the entire page rendering until we can guarantee there will be no layout shifts from font loading. |
@xiaochengh Can we render-blocking within a specific element via |
Let me reiterate that it's about tradeoffs. I think the following is a reasonable tradeoff that developers want to achieve:
I don't know exactly how many developers want to do that (other than this AMP example and @Lorp's previous comment), but given how widely
That sounds like |
Put another way, there are times when (website) users need to know when “critical” fonts are missing. Is it haram that browsers should inform users of such failures? |
@Lorp Sounds like a use case for a @xiaochengh Adding a timeout makes this a lot more palatable. That said I'd want the keyword to be a lot more obvious that it's introducing render blocking on the whole page, maybe something like I also think @yisibl's comment about render-blocking a specific element deserves investigation. For the cases where that's desired, we should make that as easy to do as render-blocking the whole page so that authors are more likely opt to render-block just the one element instead of the whole page when render-blocking the whole page isn't necessary. |
I'm fine with renaming it to I think @yisibl's comment might be out of the scope of this issue, since this issue is about reducing FOIT and CLS, which are for the entire page. Maybe it should be discussed in its own issue? |
Hi, any further thoughts on this issue? I hope we can discuss it again at the next meeting this week, and I think Xiaocheng has addressed all of the concerns, especially with an improved name and advice to UAs to use a (much) shorter timeout than other resources. |
I'm generally skeptical of this proposal. In general, it's pretty scary to add more to the web platform that intentionally makes pages load slower. People do base64 encode font data into
What could convince me that this proposal was a good idea is evidence of at least one author saying:
|
Icon fonts are an anti-pattern and we should not build new CSS features for them. |
In addition, I am still uneasy with the idea that such an icon font should ever block rendering of the page. Surely Yes, it's not guaranteed to prevent any layout shift. That's the nature of the internet, if a page is dependent on a variety of resources that may load at different speeds (or might occasionally fail to load at all). Developers still have the option of using the Font Loading API to manage fonts, and could use this to explicitly block rendering until certain resources are ready. So they don't need this proposal in order to achieve that outcome. It may make it easier, but I'm not sure that's desirable. As the original comment said, it will be footgun-ish. We shouldn't be making footguns more readily accessible. |
I think base64 encoding is a strictly (much) worse solution than this proposal in many aspects:
Also, here is an example where the author wants to make a big font render-blocking. To achieve the same purpose (block rendering, then unblock on font load or timeout), it will be much more complicated, and hence error-prone, with the Font Loading API. This is the common problem of polyfills, and the reason why we are speccing new features. I already showed in my previous comments that developers have a strong (and totally legit) need for using web fonts without causing layout shifts. And in case of a strong developer need, I think we should make it easy and risk-free to use. |
I am not convinced by the idea that a Font Loading API solution is inherently error-prone. A scripted solution makes it possible to encode the developer’s exact preferences. For instance, you can choose how long to wait (as requested in the linked example) where we would have to pick a single timeout that might or might not fit a particular purpose. I would be much happier waiting for a widely-used polyfill that we could take as a much clearer sign for determining what exactly should go in to this feature. |
Font Loading API polyfill actually has a worse loading performance, which is something I missed in my previous comment. For example: <link rel=stylesheet href="https://3p-fonts.com/cool-font.css">
<script>
document.documentElement.display = 'none';
Promise.race([
document.fonts.load('20px cool-font'),
setTimeoutAsPromise(1500), // in case connection is bad
]).then(() => document.documentElement.style.display = '');
</script>
<style>body { font-family: cool-font; }</style> Then we need to wait until the font stylesheet is fully loaded and parsed to start loading the web font. This can be much slower than my proposal, which can use a preload scanner to start loading the font much earlier. The polyfill can be improved if we also preload the font in the main document, in which case it will be as fast as my proposal. However, this isn't possible if the font URL is managed by a 3rd party provider, which is a major use case I'd like to support. |
How about we specify the timeout in the declaration as a required field? That would allow a custom timeout, and also make it clear in the style sheet what it's doing.
|
By this I guess you mean that the font URL is not knowable by the page, only the style sheet URL is? And because the style sheet can't be fetched (absent CORS headers), its text can't be inserted into the document dynamically (or the font URLs extracted from inside it). But I think the polyfill can still make it work, since it can watch for the load event of the third party style sheet: <script>
function reveal() { document.documentElement.style.display = ""; }
document.documentElement.style.display = "none";
setTimeout(reveal, 500);
</script>
<link href="https://fonts.googleapis.com/css2?family=Smooch" rel="stylesheet" onload="document.fonts.load('20px Smooch').then(reveal);">
<style>body { font: 100px Smooch; }</style>
<p>Here is my text.</p> |
I'm removing agenda+ for now while we go obtain some more evidence and details about this proposal. |
@heycam I think your code snippet has the same loading performance as mine. Both have the following timeline:
But the original proposal can achieve:
Besides loading performance, I'd also like to avoid using a parser-blocking script to call |
There's a proposal[1] to enable web fonts to block rendering, in order to reduce CLS. There's an open question regarding how long we should allow developers to hold off rendering for fonts (as we want to avoid LCP and FCP regressions) The above feature could be limited to critical & preloaded fonts, in order to reduce that risk. As such, we need to collect the following data: * The time in which all the render blocking resources are loaded, and rendering is no longer blocked. * The time in which all preloaded fonts have arrived - this assumes that preloading a font is a developer signal regarding its criticality. * The maximum time between these two - as that's a good approximation of the new FCP. * The difference between these two - as that would tell us if holding rendering for those fonts would be helpful at all, and how much it could delay FCP. This CL implements that data collection. [1] w3c/csswg-drafts#7271 Change-Id: Id4dc36413d0ad79badb71f602a1130a833a1b216 Bug: 1372346 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3937972 Reviewed-by: Stephen Chenney <schenney@chromium.org> Reviewed-by: Xiaocheng Hu <xiaochengh@chromium.org> Commit-Queue: Yoav Weiss <yoavweiss@chromium.org> Cr-Commit-Position: refs/heads/main@{#1061170}
There's a proposal[1] to enable web fonts to block rendering, in order to reduce CLS. There's an open question regarding how long we should allow developers to hold off rendering for fonts (as we want to avoid LCP and FCP regressions) The above feature could be limited to critical & preloaded fonts, in order to reduce that risk. As such, we need to collect the following data: * The time in which all the render blocking resources are loaded, and rendering is no longer blocked. * The time in which all preloaded fonts have arrived - this assumes that preloading a font is a developer signal regarding its criticality. * The maximum time between these two - as that's a good approximation of the new FCP. * The difference between these two - as that would tell us if holding rendering for those fonts would be helpful at all, and how much it could delay FCP. This CL implements that data collection. [1] w3c/csswg-drafts#7271 (cherry picked from commit 7e5b088) Change-Id: Id4dc36413d0ad79badb71f602a1130a833a1b216 Bug: 1372346 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3937972 Reviewed-by: Stephen Chenney <schenney@chromium.org> Reviewed-by: Xiaocheng Hu <xiaochengh@chromium.org> Commit-Queue: Yoav Weiss <yoavweiss@chromium.org> Cr-Original-Commit-Position: refs/heads/main@{#1061170} Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3968909 Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com> Cr-Commit-Position: refs/branch-heads/5359@{#182} Cr-Branched-From: 27d3765-refs/heads/main@{#1058933}
Currently, we don't have any easy & sound way to eliminate FOIT & layout shifts caused by web fonts:
font-display: optional
elimintes layout shifts and FOIT, but at the cost that the web font may not be used, which limits its usagefont-display
values do not reduce layout shifts at allsize-adjust
descriptor only reduces the layout shift but can't guarantee 0 CLS, and there might still be a FOIT. It's also complicated to use since we need to find out the correct value.I'm proposing adding a new keyword to the
font-display
descriptor, tentative namedfont-display: critical
, which:@font-face
a critical subresource of the style sheet, and hence makes it block theload
event of the style sheet@font-face
eagerly; other font faces are still loaded lazily when usedfont-display: block
In this way, as long as the
@font-face
is defined in a render-blocking style sheet (which everyone knows about but hadn't been specified until recently), then it will block the first render of the document, and hence eliminate FOIT / layout shifts.This feature is supposed to be used on web fonts that are truly critical, so that developers want to eliminate FOIT / CLS at a great cost of delaying rendering. It will be footgun-ish and shouldn't be used arbitrarily.
Use cases
Basic usage:
There's also a particular use case I'd like to support: making a 3rd party web font render-blocking without knowing the font url.
Developer page:
https://3p-fonts.com/cool-font.css?critical=yes:
Possible discussions
blocking=render
to the font preload<link>
to block rendering until the preload finishes?blocking=render
from preload. See more discussions atblocking
attribute on apreload
link whatwg/html#7896Link
header that preloads the fontfont-display
keyword?font-display: block
. So it doesn't make much sense to be a standalone descriptor and then interact with the otherfont-display
values. That's also the reason why its font display timeline is the same asfont-display: block
, though it's mainly for spec completeness -- if we ever need to use such a font while it's still pending, it's likely a misuse.Possible blockers
font-display: critical
font faces" without having to fully define what other critical subresources are.@tabatkins @chrishtr
The text was updated successfully, but these errors were encountered: