Skip to content
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-4] font-display: optional without relayout #4108

Closed
jakearchibald opened this issue Jul 12, 2019 · 39 comments
Closed

[css-fonts-4] font-display: optional without relayout #4108

jakearchibald opened this issue Jul 12, 2019 · 39 comments
Labels
css-fonts-4 Current Work

Comments

@jakearchibald
Copy link
Contributor

jakearchibald commented Jul 12, 2019

https://drafts.csswg.org/css-fonts-4/#font-display-desc

I originally thought font-display: optional was designed to provide web fonts in a way that avoided swapping or flash-of-invisible-text, at the expense of getting a fallback font for the first visit. This is kinda backed up by a note in the spec:

the font is used if it’s already downloaded and available, but otherwise a fallback is used for the rest of the page’s lifetime instead. The font might download in the background and be available to future page loads

However, the prose doesn't really define the behaviour.

https://static-misc.glitch.me/optional-font-load/

In Chrome & Firefox at least, the page may briefly render with invisible text (using layout from a fallback font), causing the green bar to move when the page is laid out again using the web font. This is the kind of layout instability I thought font-display: optional was designed to avoid.

Proposal:

When the browser discovers a part of the page that needs a font-display: optional web font, it must:

  1. Block rendering.
  2. Makes an "only-if-cached" fetch for the resource.
  3. Let font be null.
  4. If there's a valid font response that isn't stale, set font to that resource.
  5. Otherwise, set font to the result of getting the next item in the font-family stack.
  6. Unblock rendering (if it hasn't already been unblocked during the previous line).
  7. If there was no response, or the response was stale, make a low-priority request for the font resource (just to populate the cache for next time).

We already block rendering when getting regular font resources from disk, so blocking rendering shouldn't really be a problem here, unless we expect the above to be significantly slower than loading installed fonts.

Do you think we can safely change font-display: optional to behave similar to above? If not, do we need a new value that behaves like this?

@tabatkins
Copy link
Member

Sigh, yes, that is absolutely the intention of that value. The only reason the spec mentions "extremely small block period" for optional and fallback is because implementors complained that a 0s block period technically disallows loading from cache, if the cache is slow enough that the timer can advance before it loads.

@jakearchibald
Copy link
Contributor Author

Yeah, I think at the time we didn't have a good way to express it via fetch.

@ewilligers ewilligers added the css-fonts-4 Current Work label Aug 27, 2019
@tdresser
Copy link

Web fonts are showing up as one of the primary causes of layout instability. I'd love to be able to give web devs the advice "apply font-display:optional and this won't be a problem", but today we can't do that.

Any thoughts on next steps here?

@chrishtr
Copy link
Contributor

chrishtr commented Dec 13, 2019

I agree that we need this mode. Unfortunately, today use of a local, pre-cached web font incurs a two renders to draw most of the time, because the there is no way to make the font block the rendering entirely for a limited time.

Browsers can add heuristics to anticipate this, but there is an inherent tradeoff about whether the font blocks all of rendering that, for high-performance sites, the site author should be allowed to make.

@jakearchibald
Copy link
Contributor Author

There must be a way, as it's what we do for regular installed fonts.

@xiaochengh
Copy link
Contributor

For some context, for font-display:optional, when the web font is already cached, Chrome (80.0.3987.7) in some cases may synchronously resolve the network request during style recalc and use it immediately for rendering. This is what I observed after reloading the test case (https://static-misc.glitch.me/optional-font-load/) for a few times.

This sounds like the desired behavior, and I'd love to see it enforced by the spec.

@tabatkins
Copy link
Member

All right, pushed some new text that should make the intent much clearer:

If the font can be loaded "immediately"
(such that it's available to be used for the "first paint" of the text),
the font is used.

Otherwise, the font is treated
as if its [=block period=] and [=swap period=] both expired before it finished loading.
If the font is not used due to this,
the user agent may choose to abort the font download,
or download it with a very low priority.
If the user agent believes it would be useful for the user,
it may avoid even starting the font download,
and proceed immediately to using a fallback font.

An ''optional'' font must never
cause the layout of the page to "jump" as it loads in.
A user agent may choose to slightly delay rendering an element using an optional font
to give it time to load from a possibly-slow local cache,
but once the text has been painted to the screen with a fallback font instead,
it must not be rendered with the ''optional'' font for the rest of the page's lifetime.

@chrishtr
Copy link
Contributor

chrishtr commented Jan 10, 2020

The latest commit is not quite enough for font-display: optional I think:

  1. It says that the font should only be used if it "can be loaded 'immediately'". This needs to be clarified to allow for async loading from an on-disk cache, service worker, or other low-latency network source. I think it should be reworded to allow a "short" block period. The 100ms of bock periods elsewhere in the spec sounds fine to me.

  2. It needs to say that the font blocks all rendering until it's known that the font won't come through in the block period, not just rendering of elements the font applies to.

  3. The font needs to always be loaded, regardless of whether it applies to any elements on the page. This is necessary to avoid extra, inefficient forced style or layouts, and allow the fetch (either from cache or network) to start as soon as the font element is parsed. This also is needed to mitigate the "blocks all rendering" behavior of item 2 above.

@chrishtr chrishtr reopened this Jan 10, 2020
@chrishtr
Copy link
Contributor

Feedback from other vendors and community members is also needed for these proposed semantics.

@chrishtr
Copy link
Contributor

The rationale for allowing low-latency network loads in addition to cache is:

  • Caches sometimes have performance similar to fast networks, especially when inter-process IPC overhead is also taken into account
  • Allows for service worker caching
  • Will allow fast sites to have a chance to have perfect rendering with web fonts, even on the first load

@jakearchibald
Copy link
Contributor Author

jakearchibald commented Jan 10, 2020

A user agent may choose to slightly delay rendering an element

This seems to suggest that subsequent elements may still render.

We currently define font block period to mean 'layout the text using the fallback font but don't render glyphs'.

We need another thing that defines what browsers currently do when loading installed fonts. Is it a total render block? Eg will it block text selection throughout the page too? I'm assuming so. I'll refer to this as render block period.

Then, the 'block', 'swap' and 'fallback' strategies will have a small render block period, in addition to their font block period.

The 'optional' strategy will just have a render block period.

The tricky part is picking a number for the block time. UAs could even have a longer render block period before page load, where there's a lot of jank happening anyway.

UAs could skip the render block period if the browser is pretty sure the period limit would be reached (eg, font is not cached, and no service worker, and network looks slow).

@tabatkins
Copy link
Member

It says that the font should only be used if it "can be loaded 'immediately'". This needs to be clarified to allow for async loading from an on-disk cache, service worker, or other low-latency network source. I think it should be reworded to allow a "short" block period. The 100ms of bock periods elsewhere in the spec sounds fine to me.

I'm not going to restore the exact language that I literally just removed due to complaints about it giving bad effects when read literally. ^_^

"Immediately" is scare-quoted and then immediately defined in terms of the necessary restriction it's imposing; as far as I can tell, that restriction (that the font be available in time for the first paint) allows all of the scenarios you mentioned? I very intentionally make no reference to the source of the font, precisely so that any of these can apply if the UA considers it reasonable.

It needs to say that the font blocks all rendering until it's known that the font won't come through in the block period, not just rendering of elements the font applies to.

That's a knock-on effect of the next paragraph, dictating that loading an optional font must not cause layout to jump. (This allows subsequent elements to be rendered if the UA can determine they can't depend on the layout of the optional-font element, like if the optional-font is used in a layout-contained element or something.)

The font needs to always be loaded, regardless of whether it applies to any elements on the page. This is necessary to avoid extra, inefficient forced style or layouts, and allow the fetch (either from cache or network) to start as soon as the font element is parsed. This also is needed to mitigate the "blocks all rendering" behavior of item 2 above.

I think this is an explicit anti-goal of this value; an "optional" font that is instead eagerly pre-loaded immediately on page load isn't very optional, I think. ^_^ Note the other functionality attached to the value, allowing the UA to abort the font load entirely if they know they're on a low-bandwidth connection, for example. An "optional" font is explicitly intended to be unreliable; the feature is aimed at being maximally friendly to users at the expense of authors' design preferences.

An author can already preload fonts explicitly using the existing meta-preload functionality; combining that with optional to give you a better chance of hitting the "immediately available" window is a perfectly reasonable idea.


This seems to suggest that subsequent elements may still render.

See response to Chris's second point, above.

We need another thing that defines what browsers currently do when loading installed fonts. Is it a total render block? Eg will it block text selection throughout the page too? I'm assuming so.

It is not, at least in Chrome. Loading an installed font from the disk cache can, today, result in a flash-of-unfonted-text for a frame or two if the call is slow. I think loading the generic fonts is blocking for rendering, tho. @xiaochengh has the details; I'm probably badly recounting what I remember them telling me.

@xiaochengh
Copy link
Contributor

The font needs to always be loaded, regardless of whether it applies to any elements on the page. This is necessary to avoid extra, inefficient forced style or layouts, and allow the fetch (either from cache or network) to start as soon as the font element is parsed. This also is needed to mitigate the "blocks all rendering" behavior of item 2 above.

I think this is an explicit anti-goal of this value; an "optional" font that is instead eagerly pre-loaded immediately on page load isn't very optional, I think. ^_^ Note the other functionality attached to the value, allowing the UA to abort the font load entirely if they know they're on a low-bandwidth connection, for example. An "optional" font is explicitly intended to be unreliable; the feature is aimed at being maximally friendly to users at the expense of authors' design preferences.

How should a UA decide whether/when to load a font-display: optional font?

According to the font loading guidelines, UA should "download" a font only when "used within a document". However, we can't know whether a font is used without actually rendering the document. And if we render the document, we are basically violating the "no relayout" requirement of optional. Even if we don't deliver the frame (so that layout doesn't jump), we are still introducing extra latency that we want to avoid.

So If authors want to ensure no extra rendering while maximizing the chance that the font is used, do they have to force loading it with Font Loading API or <link rel="preload">, in combination with optional?

(Or do words "download" and "load" have different meanings here?)

@xiaochengh
Copy link
Contributor

xiaochengh commented Jan 10, 2020

I feel like there are two different use cases where we want to avoid relayout and extra rendering latency:

  • The font is really important and likely already cached. Try the best to use it before rendering with fallback
  • The font is good-to-have but optional. Use it only when it's immediately available, but don't try too hard

The latest spec revision seems to solve the latter. Should we introduce something like font-display: important for the former?

@tabatkins
Copy link
Member

However, we can't know whether a font is used without actually rendering the document.

I don't understand what you mean here; this seems obviously false. You know when a font is used after styling a document; the render is much later in the pipeline.

So If authors want to ensure no extra rendering while maximizing the chance that the font is used, do they have to force loading it with Font Loading API or <link rel="preload">, in combination with optional?

Yes. That'll give the page vital milliseconds to pull down the font (or load it from disk cache) before the page is otherwise renderable, maximizing the chance it gets selected.

The latest spec revision seems to solve the latter. Should we introduce something like font-display: important for the former?

The optional value was always the latter; the name, the description, and the mechanics were all firmly aimed at "I'd like to use this font but it's totally okay to render the text in whatever's available quickly".

font-display: important is just font-display: block, no? Or if it's not, that's because it's actually even more restrictive than block, blocking rendering of the entire page until the font is delivered (rather than just the text)?

Given that we already widely agree that block is a bad, user-hostile behavior, I don't see how an even more hostile behavior would be something we want to add.

@emilio
Copy link
Collaborator

emilio commented Jan 10, 2020

I don't understand what you mean here; this seems obviously false. You know when a font is used after styling a document; the render is much later in the pipeline.

That's false. You need to do font fallback in order to know whether a font is used. So the trigger of the font load happens much later than styling.

You could eagerly download all the web fonts that appear in any family list for all possible unicode ranges, but that's obviously not great.

@xiaochengh
Copy link
Contributor

However, we can't know whether a font is used without actually rendering the document.

I don't understand what you mean here; this seems obviously false. You know when a font is used after styling a document; the render is much later in the pipeline.

Sorry if I'm not using the accurate terms.

In that way, we still need a forced style recalc to decide whether to load the font. When to do that remains quite tricky. Besides, that's not a cheap task, and the computed style will be invalidated after the font loads (which is at least what's happening in Blink), which is something we'd like to avoid.

So If authors want to ensure no extra rendering while maximizing the chance that the font is used, do they have to force loading it with Font Loading API or <link rel="preload">, in combination with optional?

Yes. That'll give the page vital milliseconds to pull down the font (or load it from disk cache) before the page is otherwise renderable, maximizing the chance it gets selected.

The latest spec revision seems to solve the latter. Should we introduce something like font-display: important for the former?

The optional value was always the latter; the name, the description, and the mechanics were all firmly aimed at "I'd like to use this font but it's totally okay to render the text in whatever's available quickly".

font-display: important is just font-display: block, no? Or if it's not, that's because it's actually even more restrictive than block, blocking rendering of the entire page until the font is delivered (rather than just the text)?

Given that we already widely agree that block is a bad, user-hostile behavior, I don't see how an even more hostile behavior would be something we want to add.

I don't think it's another block. I'm think of the following:

  • When the @font-face rule is added
    • Start font loading immediately
    • Block rendering until the font loads, or after a short timeout (something like 100ms to match local cache latency)
  • If the font fails to load before the timeout, it enters the failure period immediately

Authors should use it only when they want to maximize the chance to use locally cached fonts while reducing latency and layout shifting.

Maybe I should have called it font-display: cache-friendly earlier to better express it 😅

@tabatkins
Copy link
Member

I don't think it's another block. I'm think of the following:

Is this actually different from:

  1. Preload your font.
  2. Use font-display:optional
  3. Get browsers to implement the "delay rendering a tiny bit to let an optional font load in" text that's already in the spec for optional.

?

@emilio
Copy link
Collaborator

emilio commented Jan 10, 2020

Also, what do you plan to do for ex units? do you plan to block styling on the font metrics being available?

@tabatkins
Copy link
Member

That's false. You need to do font fallback in order to know whether a font is used. So the trigger of the font load happens much later than styling.

You could eagerly download all the web fonts that appear in any family list for all possible unicode ranges, but that's obviously not great.

Sure, but no fallback is needed for the first font in the list, which is what I'm most concerned about here: declarations that are just font-family: webfont, sans-serif;.

(If you have a whole stack of fonts it shouldn't be a surprise that later ones, being clearly less important, might get skipped by "optional".)

@xiaochengh
Copy link
Contributor

As you mentioned earlier, optional is for low priority fonts. It looks pretty hacky to use preload + optional to handle high priority fonts.

@tabatkins
Copy link
Member

As you mentioned earlier, optional is for low priority fonts. It looks pretty hacky to use preload + optional to handle high priority fonts.

What you described isn't high priority, tho! You described a situation where a font that takes longer than 100ms never gets used. That's clearly still an "optional" font; it's nice-to-have but not vital.

optional is intended to prioritize the user experience (no layout jumping) over the authoring design intent (pretty fonts). If the author does nothing else, it can also prioritize the user's bandwidth, but that's listed as a secondary, very much optional additional effect. Preloading is a common and reasonable technique where we let the author control the user experience a little (kicking off downloads eagerly) in return for a hopefully better UX later (things being visible immediately when used).

Combining the two seems totally reasonable to me.

@tabatkins
Copy link
Member

Also, what do you plan to do for ex units? do you plan to block styling on the font metrics being available?

How do you handle ex units today, when the font is already downloaded and in cache?

@dbaron
Copy link
Member

dbaron commented Jan 12, 2020

optional is intended to prioritize the user experience (no layout jumping) over the authoring design intent (pretty fonts).

I'm not convinced this design does a good job of that. There are other aspects of user experience that are also relevant here: for example, I think users would be bothered by having bold and/or italic text in a fallback font when the primary text was in the downloaded font. (There might also be problems with accented characters for, e.g., Latin-script locales that use characters outside of Latin1, depending on how fonts are typically chunked for download.)

I'd love to be able to give web devs the advice "apply font-display:optional and this won't be a problem", but today we can't do that.

I think this might be plausible "won't be a problem" advice for pages that use only a single font face (no weight variation, no italic/oblique variation) and only use ASCII characters (or perhaps a slightly broader Latin set depending on how the fonts they use are provided). But I think anything more advanced, which is a lot of the Web, is going to involve tradeoffs here.

(And remember that a bunch of the existing design of downloadable fonts is about trying not to download a ton more fonts than needed, given the reality of multiple weights, multiple styles, and large character ranges.)

@tdresser
Copy link

Thanks for your thoughts @dbaron - the multiple font single typeface case is an interesting one.

The primary offender I've seen here has been for headings in a webfont, which are generally in a single font, without any variations. That said, most of my digging here has focused on pages with latin scripts.

I agree that that we'll need to enable the developer to make tradeoffs here. I think the proposed semantics here around optional is one configuration we should support. Does that seem reasonable? Is there a use case for the current definition of optional that we'd lose by making this change?

@jakearchibald
Copy link
Contributor Author

@dbaron

here are other aspects of user experience that are also relevant here: for example, I think users would be bothered by having bold and/or italic text in a fallback font when the primary text was in the downloaded font.

That's an important use-case, but I think it's tangential, and a much bigger problem that should probably be spun off into its own issue. Seems like you want to synchronise the loading of all faces of a particular font family. Feels like you'd need to apply this behaviour to a particular element, and you'd need to have the whole element to know what needed to synchronise, so it'd affecting streaming rendering.

@jakearchibald
Copy link
Contributor Author

@tdresser

Is there a use case for the current definition of optional that we'd lose by making this change?

I still think we need to define how browsers render when they load fonts from disk. I've never seen this create layout stability issues, but maybe there are extreme cases where it does.

Either way, this is the behaviour we want for optional, but with an aggressive timeout where it switches to a fallback.

@tabatkins
Copy link
Member

tabatkins commented Jan 14, 2020

Yeah, the "load/use all the faces of this font, or none of them" is a reasonable thing to want, but it's definitely an orthogonal issue; you'd want that control for non-optional fonts as well. So unless there's an important connection to the current topic that I'm not seeing, I'd like to push that to another issue.

I still think we need to define how browsers render when they load fonts from disk. I've never seen this create layout stability issues, but maybe there are extreme cases where it does.

Per @xiaochengh, loading from memory cache is always fine, but loading from disk cache sometimes (often?) misses the first frame's layout/painting, so the second frame has a layout jump. It might be that a single frame of jump is perfectly fine tho; I want to hear from people currently experiencing that (we've got some internal customers for this behavior, apparently, so I'll be talking to them), and if it's okay, make sure the spec text is flexible enough to allow for it. (Currently it's not; if you miss the first paint you'll never use the font. It just lets you delay the first paint a little if you want to give it a chance to finish loading.)

@tabatkins
Copy link
Member

Okay, just wrapped up a meeting with @chrishtr and @xiaochengh to discuss the minutiae of our implementation. I'll summarize here, with our preferred conclusion, and other browsers can chime in if there's a disconnect with their own impls.

  • When you're processing styling and realize a font is needed, we kick off a load. If the font is in memory cache (generally per-tab), this load is synchronous, and takes <1ms. Otherwise (if it's in disk cache, or not cached at all) the load is asynchronous, and we proceed without the font for this frame. (Next frame it might get picked up, assuming it loaded fast enough; disk cache should generally return before a second frame is started.)

  • Memory cache is pretty ephemeral; usually it'll stick around as long as a tab is alive, but it can evict at any time (so subsequent same-origin navigations might have it available, or might not). If you close and re-open the site, it'll usually, but not always, be evicted.

  • This means that, if we don't delay the render process, much of the time an optional font will be skipped, especially on the first navigation to a page.

This matches up with what I intend the value for; the way I use it on my blog, for example (as a pure aesthetic upgrade to heading and code font), it's perfectly okay for it to often get skipped and render with the system fonts.

But some use-cases, reasonably, would like a slightly greater assurance that the font will be used. It seems to us that a reasonable signal is whether the font is preloaded - that's a pretty strong signal that the font is expected to be used and important enough to start loading early.

So our plan is:

  • make sure that font preloads cause the font to be put in the tab's memory cache

  • track which fonts are being preloaded this way. During the "load font" check, if the font's not currently in memory cache, check if it's on the list of preloaded fonts. If so, do a blocking load for a small amount of time (20-30ms, maybe?) to give it time to finish loading into memory. Keep a bit around to track whether this happened, so it can't reoccur serially due to multiple fonts needing loading; it only delays once, and if additional preloaded fonts aren't ready yet, too bad.

  • otherwise act like optional normally does

This means that if you preload your optional font, there's a very high chance that it'll be used as long as it's been cached appropriately (or it's served off of a fast SW, or even network-loaded from an extremely fast network); the only time it'll likely be skipped is on first visit when it has to be network-loaded.


In terms of spec changes, I think I can get away with just adding a SHOULD about preloaded fonts, probably phrased more vaguely as "fonts for which the page has signaled ahead of time are likely be used", keying the "delay the first paint of the page" off of that.

How does this sound to people? Do other browsers have loading architectures that differ significantly from what I described, and would require different handling?

@chrishtr
Copy link
Contributor

I think this captures almost all of it (doesn't seem to capture cached fonts that happened to not have a preload meta tag?).

How about this:

Let's call a font cached-locally if no network request (Service Worker not allowed either) need be done to get the resource.

Let's call a font short-render-blocking if it is referenced by a preload tag in the HTML document, or is cached-locally.

If, while parsing an HTML document, we encounter a reference to a short-render-blocking font, then the User Agent should delay subsequent rendering for a short period (< 100ms) in order to give that font time to load. If a font is cached-locally, the User Agent should move the font into memory as soon as it is discovered in the stylesheet.

That's it. If a developer wants their font to have the highest likelihood of displaying on cold and warm loads, and never have double renders due to that font, they should put something like this in their HTML:

HTML:

<-- This causes the font network fetch to start as soon as possible, supports caching
in the local HTTP cache or service worker, and causes the font to be
short-render-blocking -->
<link rel="preload" href="my-font.woff" as="font">

...

<link rel="stylesheet" href="my-stylesheet.css">

...

Content that depends on my-stylesheet.css

my-stylesheet.css:

@font-face {
  font-family: ExampleFont;
  src: url(my-font.woff) format('woff'),
 
  # This causes the font to never cause double rendering
  font-display: optional;
}

Perhaps this should also apply to all web fonts. font-display: optional is perhaps special only in that we should never render twice due to it, regardless of whether it was available in memory for the first render.

@xiaochengh
Copy link
Contributor

Some more recap of our meeting...

There can be some issues if we try to load all cached-locally fonts on parsing, especially when using a web font library. We can end up loading too many unused fonts into memory. We are still good if we only delay rendering for the fonts being preloaded.

Also @drott for insights.

@chrishtr
Copy link
Contributor

Good point. Perhaps we should say may instead of should for "move the font into memory as soon as it is discovered in the stylesheet", and only leave should for ones that have a preload tag.

This would allow UAs to only do so if there is memory budget, or there is good reason to think the font is definitely needed, or some other clever heuristic. Or allow them to just not do that and still be compliant.

@dbaron
Copy link
Member

dbaron commented Jan 23, 2020

@tabatkins wrote:

Yeah, the "load/use all the faces of this font, or none of them" is a reasonable thing to want, but it's definitely an orthogonal issue; you'd want that control for non-optional fonts as well.

Except I'm not sure the "load" part of it really is reasonable, given large numbers of faces (e.g., for weights or character ranges).

It might be that some faces for a font are already in use (and were, say, already cached), and a change occurs to require an additional face (say, new text in a new character range, or something in a different weight or style). It seems like in that case you (a) probably don't want to flash the already-loaded fonts back to their fallback either temporarily or permanently, but different developers may have substantially different feelings between (b) showing fallback temporarily for the new characters and then switching versus (c) showing fallback permanently for the new characters but continuing to use the downloaded font for the faces that were already displayed.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed font-display.

The full IRC log of that discussion <fantasai> Topic: font-display
<astearns> github: https://github.com//issues/4108
<TabAtkins> https://github.com//issues/4108
<TabAtkins> github: https://github.com//issues/4108
<fantasai> TabAtkins: When I originally drafted the font-display values, the last one, optional, I intended to be as much as possible "make the user's experience smooth even if they don't get pretty fonts"
<fantasai> TabAtkins: Wasn't super clear in the spec, and implementers came up with answers that don't satisfy that goal
<fantasai> TabAtkins: Within first 100ms or less, still creates a layout jump
<fantasai> TabAtkins: Jake was running into the same problem
<fantasai> TabAtkins: wanted smooth page display without any layout jank, and wasn't getting it
<fantasai> TabAtkins: I believe the current text addresses it, perhaps with an extra comment
<fantasai> TabAtkins: Would like some discussion from y'all
<TabAtkins> https://drafts.csswg.org/css-fonts-4/#valdef-font-face-font-display-optional
<fantasai> TabAtkins: My text atm, if font can be loaded "immediately", can be used, otherwise ignore it
<fantasai> TabAtkins: can't be used for the rest of the page's lifetime. Refresh page, can try again
<fantasai> TabAtkins: Paried with some additional info
<fantasai> TabAtkins: MUSTNOT cause the layout of the page to jump
<fantasai> TabAtkins: allowed to delay initial rendering if needed to load the font
<fantasai> TabAtkins: specifically, we have some issues wrt how to do in Chrome, can put more detail in the spec
<fantasai> TabAtkins: Anyone have comments on details?
<fantasai> TabAtkins: If in memory cache for tab, use the font. Otherwise we go to disk, takes too long, skip it
<fantasai> TabAtkins: Once loaded, may ro may not be present for future navigation depending on cache state
<fantasai> TabAtkins: Cannot depend on font ever being there
<fantasai> TabAtkins: Want to make sure that all font pre-loads bring the font into the memcache
<fantasai> TabAtkins: track which fonts are there
<fantasai> TabAtkins: When preloaded font is optional, delay loading to give time to get off disk -- or extremely fastnetwork
<fantasai> TabAtkins: otherwise, don't delay
<fantasai> TabAtkins: If you don't do anything special on your page, just say font is optional
<fantasai> TabAtkins: almost guaranteed to not have font on first page load
<fantasai> TabAtkins: chance of available on future page loads, but not guaranteeed, e.g. if on device with small cache
<fantasai> TabAtkins: If it's a preloaded font, then very likely to be load on future navigations because we will delay rendering to load into memory cache
<heycam> q+
<fantasai> s/small cache/small memory/
<fantasai> TabAtkins: because preloading is a hin that something is important enough to kick of load asap
<fantasai> TabAtkins: That seems to satisfy the use cases for optional that we want to hit
<fantasai> TabAtkins: allowing pl to have completely jank-free font-optional experience
<fantasai> TabAtkins: Try your best to use it, but prioritize no jank
<myles> q+
<astearns> ack heycam
<fantasai> heycam: not sure how much is normative vs heuristics
<fantasai> heycam: but seems a bit weird to me that you have two different behaviors wrt disk cache
<fantasai> heycam: without preloaded fonts, disk cache is deemed too slow, but link rel=preload it's ok?
<fantasai> TabAtkins: Not so much it's too slow, but don't want to incur cost of delaying rendering unless indication that it's important to you
<fantasai> heycam: is this because checking whether the font is in the disk cache is also slow? is it the checking, not the loading?
<fantasai> TabAtkins: in our implementation, memcache check is fast enough to be synchronous, whereas disk cache is async, we kick off without loading
<fantasai> heycam: disk cache can be very fast when it's not too big
<fantasai> TabAtkins: definitely cool to keep things vague enough that UAs can adjust
<fantasai> TabAtkins: but want preload to cause delay
<fantasai> heycam: Other thing I was going to say is I'm not really a big fan of optional
<fremy> q?
<astearns> ack myles
<fantasai> myles: I have a lot to say so split into pieces
<fantasai> myles: Firstly, wnat to make sure it's clear that WebKit will never ever defer first load. So that has to be not a case. that's our philosophy
<fantasai> myles: Can you be super clear about which, you listed like 3 main pieces
<fantasai> myles: one is deleted number 100ms and replaced with words
<fantasai> myles: one is never cause layout jank
<fantasai> myles: last one was about preloading
<fantasai> myles: which are normative
<fantasai> TabAtkins: first two are normative, in the spec
<fantasai> TabAtkins: third one I would like to put in the spec
<fantasai> myles: The spec shouldn't use words mem cache and disk cache, those are impl details
<fantasai> myles: still not guaranteed to get the font even if preload
<fantasai> myles: there's nothing testable here
<fantasai> myles: So I think this should be evangelism, not in a spec
<fantasai> myles: "for our browser, you will get better results if you preload"
<fremy> q+
<fantasai> TabAtkins: Talking to internal customers who want no jank on initial load
<fantasai> TabAtkins: if they don't get it, and instead block loading page, that's a worse user experience
<fantasai> TabAtkins: so having some reasonable assurance of similar behavior among browsers would be worthwhile
<fantasai> TabAtkins: ultimately stochastic, cna't depend on it, but difference between 80% and 10%
<fantasai> myles: Are your internal customers only considering Chrome, or also other browsers?
<fantasai> [unminuted]
<fantasai> myles: Other piece I want to mention, dbaron brought up a super valuable point
<fantasai> myles: I think it supports what heycam said as is last sentence
<fantasai> dbaron: One of my concerns with this is that a lot of stuff around downloadable fonts is designed to deal with the possibility that you have a pretty large number of faces for various reasons
<fantasai> dbaron: You might have fonts egmented by character range, multiple weights 4-6 of them, a lot of the mechanisms around downloadable fonts were designed to load lazily
<fantasai> dbaron: define a library, fetch the ones you need
<fantasai> dbaron: so some of this pre-loading stuff...
<fantasai> dbaron: there are cases where you just use one font
<fantasai> dbaron: those cases probably bias towards symbol font hacks, and text that is all Latin
<fantasai> dbaron: and maybe things are more complicated in non-Latin languages, or things that use more weghts and stuff like that
<fantasai> dbaron: one of my big concerns about font-display is that it is per-face
<fantasai> dbaron: can have situation where your regular font cached and bold font isn't
<fantasai> dbaron: and using downloadable font permanently for regular weight and local font for bold might be worse than all of the other cases
<fantasai> TabAtkins: Those can still totally happen is the font load just doesn't occur
<fantasai> dbaron: if you have network failures can happen
<fantasai> myles: optional makes it more likely that it fails
<myles> q_
<myles> q+
<astearns> ack fremy
<fantasai> fremy: As an author, at this point, with current proposal for optional, I would be very unlikely to use it. Because more likely to not get your font than to get it
<fantasai> fremy: why bother?
<fantasai> fremy: Only if user keeps using your site regularly, not going to be in memory
<dbaron> s/other cases/other cases (including flashing, or fallback for all the fonts)/
<fantasai> fremy: not unless constantly in use
<fantasai> myles: no, it's valuable for web sites while you're navigating through mutiple pages on the site
<fantasai> TabAtkins: won't work on single-page app, don't use it in one
<AmeliaBR> q+
<fantasai> fremy: You're extremely unlikely to get the font, then why bother? You are going to download the font anyway.
<fantasai> TabAtkins: Your case is a single-page app that gets closed and reopneed every day
<astearns> zakim, close queue
<Zakim> ok, astearns, the speaker queue is closed
<fantasai> TabAtkins: preload signal intention is that, if it's still in your disk cache, it will likely get used the 2nd day and subsequent days, because giving it enough time fo rthat
<fantasai> fremy: Safari said they don't want to block rendering
<fantasai> myles: Internal clients care that usage gets up to 80%, but we're absolutely not going to block rendering
<fantasai> myles: That's not going to work in Safari
<fantasai> TabAtkins: assuming ppl aren't using optional, then comfortable with janky 2 frames and then stable layout
<fantasai> myles: I'm OK with you've shown the page with fallback font then never show the page with loaded fonts
<fantasai> TabAtkins: ...
<fantasai> TabAtkins: If it's likely that optional will never give them assurance to see font, then they'll use JS to delay rendering manually
<fantasai> TabAtkins: or will use one of the values that wll cause jank anyway
<fantasai> myles: This is an intractable problem. We can't never delay rendering and have the disk cache be able to serve tehse requests
<fantasai> myles: given that these customers will never get what they're aiming for, I don't think that the association with preloading makes sense
<fantasai> TabAtkins: Want to check understanding
<fantasai> TabAtkins: website authors using fallback, getting a couple frames in one font and then frames in other font, it's better than getting a few frames of nothing and thn getting the page?
<fremy> q+
<fantasai> s/thn/then/
<myles> q-
<astearns> ack AmeliaBR
<fantasai> AmeliaBR: I've heard a lot of confusion of what's the purpose of this value
<fantasai> AmeliaBR: Tab said the goal was to avoid jank
<fantasai> AmeliaBR: If that's the goal then maybe other smarter ways this can happen, like waiting until doing a major repaint anywan and then block in the font
<fantasai> AmeliaBR: catches the single-page app update situation
<fantasai> AmeliaBR: but I don't kno if that's the best
<fantasai> AmeliaBR: As a user, the idea of downloading fonts but then not using them unless I happen to close the page and re=-open it before it gets lost from the cache, that's not a great use of my data plan
<fantasai> TabAtkins: UA is allowed to skip downloading if it thinks that's reasonable in the circumstances, and metered data is definitely such a circumstance.
<fantasai> astearns: Done for today
<fantasai> Meeting closed.

@chrishtr
Copy link
Contributor

I wasn't there in person, and AIUI this issue will be discussed again tomorrow, which is great. In the meantime, I'd like to repeat the three driving use-cases from my perspective:

  1. Provide a simple way for developers to make sure important, locally-cached web fonts apply on first render of the page, and to allow for this possibility if the font network request returns quickly.
  2. Maximize speed of the final loaded state of pages that use web fonts, by avoiding two renders on load
  3. Provide a way for sites to minimize layout instability as it relates to web fonts, without large negative performance impact (**)

(*) Note: this does not say anything about cases where the font is not available locally on the device.

@jakearchibald
Copy link
Contributor Author

+1. Also, it'd be nice if the solution also prevented layout instability when loading an inlined base64-encoded font.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed font-display: optional, and agreed to the following:

  • RESOLVED: change normative text for font-display optional to say that the font should never change rendering of the page if it would you'd still just treat the load as failed and don't use it again
  • RESOLVED: Add notes for implementors and authors to the spec, specific contents TBD
The full IRC log of that discussion <emilio> topic: font-display: optional
<astearns> github: https://github.com//issues/4108
<emilio> TabAtkins: I think we probably have a plan even if WebKit disagrees with this
<emilio> ... I don't think we will get into a deadlock
<emilio> ... As I said, the main goal of optional was to avoid layout shift
<emilio> ... layout shift is really annoying and font-display: optional aims to solve that
<emilio> ... another is performance
<emilio> ... turns out that our data is that our the extra style and layout that the font load triggers delays stable layout significantly
<emilio> ... and if you waited one or two frames you'd just get the final layout faster than if you did everything eagerly
<emilio> ... and getting reasonable assurance about getting local fonts or downloaded fonts getting caches in getting the details of this right
<emilio> ... and interesting quirk I found yesterday, some of our internal teams are using font-display: optional on iOS because caches there are usually faster than most android phone
<emilio> ... so it usually hits the cache faster and if so... that's great
<emilio> myles: I see your strategy :P
<emilio> TabAtkins: Proposal is that for preloaded fonts, as soon as you see the preload tag, or as soon as you see the font-face rule that would refer to them, pull them be into the memory cache
<emilio> ... for normal pages you should be able to get all the fonts you're using
<emilio> ... when you start loading, if any of the loads triggered not from the cache, you're allowed to delay rendering before starting your first layout
<emilio> ... and if you can't (too slow / whatever), then you discard the load and never use them again for the lifetime of the page
<emilio> myles: I don't think any of those changes require any changes to any spec
<emilio> ... the distinction between memory / disk cache is not something that should be on any spec
<emilio> ... and if you use fuzzy words I don't think they would mean anything
<emilio> ... specs doesn't forbid Chrome to delay rendering
<emilio> ... so it's untestable, doesn't need to be in any spec, I'd be ok with no change
<emilio> TabAtkins: I don't think I disagree with you, but it'd be good to hint to other implementations about how you can achieve a high quality implementation for this feature
<emilio> myles: yeah that's good, but shouldn't be normative
<emilio> TabAtkins: ok, compromise proposal: We remove normative text about delaying rendering or what not, and we only state that optional fonts must not cause layout jank
<emilio> ... and then provide notes to implementations
<emilio> chrishtr: I think the spec must be clear that it's acceptable to delay rendering rather to do partial rendering
<emilio> TabAtkins: yeah I'll put that in
<emilio> myles: in the notes
<emilio> TabAtkins: in the notes
<emilio> myles: you can totally describe the intended use case in the spec
<emilio> chrishtr: right now our implementation of font-display: optional can cause two layouts. I don't think it show
<emilio> TabAtkins: yeah that's the normative text described above means
<emilio> ... well as long as pixels don't move you're fine
<emilio> myles: I think that's an important distinction
<emilio> ... if you want you can do layout every frame
<emilio> chrishtr: agreed
<emilio> fremy: when you have sheets in a document you never render before they load right?
<emilio> TabAtkins: if you see them early enough or such
<emilio> fremy: what I want to say is that there is a precedent for this
<emilio> TabAtkins: proposed text is to change normative text for font-display optional to say that the font should never change rendering of the page
<emilio> ... if it would you'd still just treat the load as failed and don't use it again
<emilio> fremy: I don't really understand what's the difference between the font-face behavior and the stylesheet behavior
<emilio> emilio: the main difference is that for stylesheets you statically know it applies, but to determine the fonts you need is that you need to do layout and styling
<emilio> chrishtr: I think the main proposal to web devs is to add a <link rel=preload> for important fonts and then use the font-display: optional to avoid the jank
<fantasai> I think it's important to distinguish it from the block behavior
<emilio> astearns: objections to the proposed resolution
<emilio> ?
<fantasai> You don't want to make the user wait more than a brief, mostly-unnoticeable moment in the case of font-display: optional
<fantasai> otherwise it's not really "optional"
<TabAtkins> fantasai, oh yes it's distinguished already; i'm just gonna remove a bit of semi-normative text
<fantasai> ok
<emilio> RESOLVED: change normative text for font-display optional to say that the font should never change rendering of the page if it would you'd still just treat the load as failed and don't use it again
<emilio> jfkthame: the phrasing about the suggestion about <link rel=preload> about an important font seems a bit off to me
<emilio> ... because tagging it as font-display: optional means that it is _not_ an important font
<emilio> TabAtkins: you'd get the desired thing most of the time assuming a high-quality implementation
<emilio> astearns: there are a couple more assumptions there, like fast network, fast disk, no contention...
<emilio> chrishtr: we could probably add the <link rel=preload> bit separate from font-display: optional
<emilio> RESOLVED: Add notes for implementors and authors to the spec, specific contents TBD

@svgeesus
Copy link
Contributor

svgeesus commented Nov 12, 2020

@tabatkins can you propose text for this resolution (as you were more involved in the issue discussion) or do you want me to take a crack at it? Or @jakearchibald want to propose text? Can be here in the issue for discussion, does not need to be a PR.

@tabatkins
Copy link
Member

Looking over the spec again, the previous normative text (from Jan 6 2020) already addresses the first resolution.

I've just added the requested note suggesting some heuristics, while making it clear that the heuristics cannot be relied upon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-fonts-4 Current Work
Projects
None yet
Development

Successfully merging a pull request may close this issue.