css-font-rendering
css-font-rendering copied to clipboard
Separation of concerns
We filmed a little show this morning where I spoke about this proposal. I had a hard time working out how I was going to explain what the number part of the proposal, I think that's because it's a separate thing.
How about separating it out?
@font-face {
/* ... */
font-rendering-wait-timeout: [duration];
font-rendering-swap-timeout: [duration];
/* or for short… */
font-rendering: [wait duration]/[swap-duration];
/* or for shorter… */
font-rendering: [keyword];
/* The current Firefox/Chrome/Opera behaviour: */
font-rendering: 3s/infinite;
font-rendering: timeout;
/* The current IE behaviour: */
font-rendering: 0s/infinite;
font-rendering: immediate;
/* The current Safari behaviour: */
font-rendering: infinite;
font-rendering: wait;
/* The Guardian/Smashing Mag approach */
font-rendering: 0s/0s;
font-rendering: race;
}
@tabatkins is 'infinite' and num/num appropriate here?
@jakearchibald I don't follow what you're proposing here.. You're introducing new concepts like immediate, wait, race?
Perhaps, when the discussion over at https://github.com/KenjiBaheux/css-font-rendering/issues/6#issuecomment-62854096 concludes, what triggered this particular issue will have been taken care of.
@igrigorik the keywords are there as equivalents to the line above (flexbox does this too http://dev.w3.org/csswg/css-flexbox/#valdef-flex-auto)
So yeah, I think I missed the explanation here.
font-rendering-wait-timeout - how long we're prepared to wait before rendering this font (for @font-face blocks) or text within the selected element. 'block' could be a better name.
font-rendering-swap-timeline - the timeframe within we're prepared to swap from one font to another (either for this font or all text within the selected element).
font-rendering: [wait duration]/[swap-duration]; is a shortcut to providing both.
Keywords provide easy-to-read versions of common behaviour, such as:
timeout==3s/infinite- the current Firefox/Chrome/Opera behaviourimmediate==0s/infinite- the current IE behaviourwait==infinite/infinite- the current Safari behaviourrace==0s/0s- the trick the Guardian do using LocalStorage
There isn't a way to do "never fall back" here, I was thinking that could be done better with font-family: icon-font, none.
Ahhh, OK, now I'm with you. This is an interesting pattern... I like it. Couple of thoughts and questions:
- s/wait/block.. i.e. font-rendering-block-timeout.
- I believe this covers all the same use cases as https://github.com/KenjiBaheux/css-font-rendering/issues/6#issuecomment-62994437, plus the additional use case of combined block+swap. Correct?
- I'm not a fan of (queue bikeshed theme song) of some of the keywords: immediate -> progressive? wait -> block?
Last but not least, what are the behaviors of various patterns if request fails?
s/wait/block.. i.e. font-rendering-block-timeout.
Works for me. I discounted it originally, because I thought font-rendering-block-timeout might look like it blocks for that duration even if the font loads quickly, but it doesn't (not any more than 'wait').
I believe this covers all the same use cases as #6, plus the additional use case of combined block+swap. Correct?
It doesn't support "render nothing on fail", unless you're interested in font-family: icon-font, none. Keen to hear @tabatkins thoughts on whether that's a good way to do it.
I'm not a fan of (queue bikeshed theme song) of some of the keywords: immediate -> progressive? wait -> block?
Those are both better names.
@igrigorik If the request fails based upon the behavior of various patterns, then how about "re-rendering" them?
@ashumeow re-rendering using which font?
@jakearchibald
font-rendering-wait-timeout
The fonts are rendered, then the above command is used until it's done. Like @igrigorik said:- what if request fails? So, then how about going for re-rendering? Re-rendering --- rendering again. Cons--- Time delay. Yet, it could be good to go instead of getting request failure. So, what are your views on it? =)
It doesn't support "render nothing on fail"...
I think we can make that work. How about...
@font-face {
/* ... */
font-rendering-wait-timeout: [duration];
font-rendering-block-timeout: [duration];
/* or for short… */
font-rendering: [block duration]/[swap-duration];
/* or for shorter… */
font-rendering: [keyword];
}
0s/0s: first font wins.0s/1s+: render immediately, allow swap for up to X seconds. (failed req is a noop)0s/infinite: render immediately, allow swap indefinitely. (failed req is a noop)1s+/0s: block for X seconds, use fallback if timeout or request failed.1s+/1s+: block for X seconds, allow swap for Y seconds; use fallback if X times out or request fails.1s+/infinite: block for X seconds, allow swap indefinitely; use fallback if X times out or request fails.infinite/0s: block until desired font is available, use fallback if request failed.infinite/1s+: block until desired font is available, use fallback if request failed (same as above)infinite/infinite: block until font request completes, do not use fallback.
I think that covers everything?
@ashumeow
.selector {
font-rendering-wait-timeout: 10s;
font-rendering-swap-timeout: 15s;
}
- If the font takes 5s to download you'll get blank space for 5s, then the web font
- If the font takes 13s to download you'll get blank space for 10s, then a fallback for 3s, then a switch to the web font
- If the font takes 14s to download you'll get blank space for 10s, then a fallback. The web font will complete its download & cache, but it will not be used within
.selector - If the font takes 5s to fail to download (network error or invalid response) you'll get blank space for 5s, then the fallback
- If the font takes 13s to fail you'll get blank space for 10s, then a fallback
- If the font takes 14s to fail you'll get blank space for 10s, then a fallback
@jakearchibald Thumbs up!
@igrigorik
infinite/infinite: block until font request completes, do not use fallback.
It's odd that infinite/infinite is so different to infinite/99999999999999s in terms of handling failure, but if I'm the only one bothered by that, no problem! I take it you're not keen on font-family: icon-font, none as a way to fall back to no-font.
To be fair there's still some oddness in font-family: icon-font, none as none will have to get layout metric from the next font in the chain, serif in that case.
The basic idea here, if I'm understanding it right, is that every long font load conceptually goes through three phases, in order:
- Don't show anything yet, I'm sure the font'll show up soon.
- Fine, show the fallback, but swap in the font I want when it's loaded.
- Fuck it, just stick with the fallback, we'll try again next page load.
With the two timeouts giving the boundaries between these phases. Correct?
The current proposal collapses some of these cases together, is all:
optional==0s / 0sswap <time>==0s / <time>mandatory <time>==<time> / infinite
In other words, it assumes that if you're willing to hide the text at all, it's probably important enough that you always want to swap it out when it finally arrives. It also provides reasonable-ish defaults for you to use when you don't have a better idea of what you need.
First, am I missing anything? If not, do you think the possibility of having both a "blank period" and a finite "swap period" for a given request is worth the added complexity? Can you give a use-case where it's worthwhile?
@jakearchibald I think infinite/0s+ is OK. I was missing some cases, updated: https://github.com/KenjiBaheux/css-font-rendering/issues/11#issuecomment-63112270
@tabatkins that sounds about right. Re, block and swap: I would really like to use the desired font, and I'm willing to wait for X seconds. That said, if you end up getting it within Y seconds, do apply it still... I think it's as reasonable of a use case as any other.
I like this proposal. It does a good job of enumerating all the various scenarios - https://github.com/KenjiBaheux/css-font-rendering/issues/11#issuecomment-63112270 - in a consistent and terse way.
@tabatkins It's possible of having both a "blank period" and a finite "swap period" for a given request. As an use-case:- When the user accesses the page, then the page gets loaded, where the web fonts are rendered. While rendering, like as @jakearchibald said in comment --- https://github.com/KenjiBaheux/css-font-rendering/issues/11#issuecomment-63112908 When the request is about to fall, the fonts does not correspond to a visible mark occupying a page area, which is the blank space. Unless the fonts takes time to fail, then we get a blank space, thereby a fallback. If it attains within 14secs, then it successfully swaps. =)
Yeah, I understand what will happen, I'm just not convinced it's actually a sufficiently worthwhile case that we need to cater for it. Jake's proposal is somewhat more complicated than the current proposal, and I'd like to see some actual justification of the added complexity.
Again, the assumption of the current proposal is that if the font is important enough for you to want to blank the text while you wait, then it's probably important enough that you want it to display when it finally comes in, regardless of when that is. Can anyone give an example of where you'd legitimately want to blank text, but after some time, just give up entirely? (Part of this may be that I don't think blanking text has much use-case anyway, beyond icon fonts. And you definitely want icon fonts to swap in when they finally arrive.)
Again, the assumption of the current proposal is that if the font is important enough for you to want to blank the text while you wait, then it's probably important enough that you want it to display when it finally comes in, regardless of when that is. Can anyone give an example of where you'd legitimately want to blank text, but after some time, just give up entirely? (Part of this may be that I don't think blanking text has much use-case anyway, beyond icon fonts. And you definitely want icon fonts to swap in when they finally arrive.)
To answer your question: personally, I can only think of icon fonts.. and not rendering if req fails or can't complete was one of the explicit use cases highlighted by @KenjiBaheux in original proposal.
That said, I don't think above is the actual difference in this proposal. You can get same block and don't render with block infinite - see https://github.com/KenjiBaheux/css-font-rendering/issues/6#issuecomment-62994437. The key difference is the "block for X, but allow swap for Y", which the other proposal can't do... How important that use case is, I'm not sure. But from a completeness standpoint, it seems like a reasonable pattern.
"block for X, swap for Y" is precisely what I'm talking about, and can't think of a use-case for.
@tabatkins
do you think the possibility of having both a "blank period" and a finite "swap period" for a given request is worth the added complexity? Can you give a use-case where it's worthwhile?
We're seeing The Guardian & Smashing Magazine doing the equivalent of 0s/0s using localstorage. With a none-hacky solution, I can imagine 0.5s/0.5s being a reasonable trade-off, so those with super-quick connections get the fonts on first load.
Jake's proposal is somewhat more complicated than the current proposal
I made this proposal after struggling to describe the [behaviour] [timeout] model. The problem there is the purpose of timeout varies depending on the behaviour. If the behaviour is wait, the timeout is "the maximum length of time to block rendering before falling back", if the behaviour is swap, the timeout is "the time window within which swapping is acceptable". Since they're completely separate & can be used together, I thought it might be easier to understand if they were distinctly separate.
I don't have a use-case where Y > X and X < infinite, such as 1s/2s. The separation was more about explaining the system.
We're seeing The Guardian & Smashing Magazine doing the equivalent of 0s/0s using localstorage.
Yeah, 0s/0s is captured by the optional keyword in the current proposal. (It's actually slightly more forgiving than 0s, but close enough.)
I can imagine 0.5s/0.5s being a reasonable trade-off, so those with super-quick connections get the fonts on first load.
Is this significantly different than swap .5s? The difference is that in the first 500ms you hide the text, rather than starting to render with fallback.
I made this proposal after struggling to describe the [behaviour] [timeout] model. The problem there is the purpose of timeout varies depending on the behaviour.
Maybe we can make this easier by fiddling with the keywords. The swap timeout indicates when you stop swapping. Maybe instead of mandatory we can do hide, as the timeout indicates when you stop hiding?
If we ever do come up with use-cases for non-zero hide + non-infinite swap, we can achieve that by letting you combine the keywords:
font-rendering: hide 1s swap 3s;
Is this significantly different than swap .5s? The difference is that in the first 500ms you hide the text, rather than starting to render with fallback.
That's the significant difference. You don't get the jarring swap. Firefox went for 3s/infinite, Guardian went for 0s/0s, seems reasonable to want a combination of the two.
If we ever do come up with use-cases for non-zero hide + non-infinite swap, we can achieve that by letting you combine the keywords:
font-rendering: hide 1s swap 3s;
I actually wrote that in my initial post then realised it was like saying margin: left 30px right 30px, it was mixing properties into the values which didn't feel very CSS-like. That's how I came to two properties.
@tabatkins to turn the question around, why should we disallow "block for X, swap for Y"? It doesn't seem unreasonable: I'm willing to suffer some pain (no text) and wait for up to X seconds, but I still want you to eventually apply the desired font if you can't meet my first deadline.
That's the significant difference. You don't get the jarring swap. Firefox went for 3s/infinite, Guardian went for 0s/0s, seems reasonable to want a combination of the two.
Okay, so you're offering a use-case for "hide initially, but fuck it if it takes too long". I guess that a zero length swap period is conceptually different from a finite non-zero length, and can be defended independently.
I actually wrote that in my initial post then realised it was like saying margin: left 30px right 30px, it was mixing properties into the values which didn't feel very CSS-like. That's how I came to two properties.
Nah, it's just a complex value. We split properties apart only when they get too complex to be easily read in one property, or when they can usefully cascade independently (that is, it's reasonable to imagine someone wanting to change one while leaving the other one alone). Neither of those are the case here (it's simple, and these are tightly tied together), so it's better to keep them together.
My overall goal here is to try and avoid confusing or unnecessary stuff in the property. I want this to be as easy to use correctly as possible. It looks like there are enough cases to address that relying purely on keywords might be confusing. In that case, I'm leaning toward Jake's proposal, just with closer to our original keywords:
font-rendering: <hide-duration> <swap-duration>;
font-rendering: optional; /* 0s 0s */
font-rendering: swap; /* 0s 3s; */
font-rendering: mandatory: /* 3s infinite */
If you want to customize the timeout, you drop down and specify the two timeouts directly. The keywords just describe general strategies with good default values. (In fact, the exact timeout values might even be UA-specific, so they can tweak them over time. In that case we'd need to preserve the keyword, rather than translating it into timeout values.)
And, per F2F discussion, we'd also still need an "auto" keyword, which does whatever the default behavior is, which can sometimes be more complex than we allow here.
(I've removed the / here - it doesn't do anything useful, and we only deploy / when we absolutely need to. I also think it's easier to imagine these as successive durations, not timings from the same zero point; it means you don't have to deal with the error case of the swap timeout expiring before the hide timeout.)
@tabatkins I think we can leave 3s infinite out in favor of auto? Instead, I'd use mandatory as shortcut for infinite/infinite - I think the name actually fits! Other than that, lgtm.
I don't like leaving out a meaningful keyword in favor of "auto". It's less clear for authors, even if "auto" is close or identical to "mandatory".
And infinite infinite is a user-hostile thing that we shouldn't make easy. ^_^ That's the Safari behavior, booooooo.
Fair enough. I'm just hesitant of standardizing block=3s behind "swap".. It's an arbitrary value, and arbitrary values have a tendency to be (ab)used a lot once they make it into specs. Perhaps we can omit it entirely? I'd rather developers think about this (not just resort to a keyword) and apply meaningful timeouts for their particular use case... On the other hand, If they don't want to think about this, they can just use the default UA behavior.
Along those lines... If we drop "swap", and we don't want to promote "mandatory" (which I agree with), we're only left with "optional". Perhaps we can skip the keywords altogether?
I'm just hesitant of standardizing block=3s behind "swap".. It's an arbitrary value, and arbitrary values have a tendency to be (ab)used a lot once they make it into specs.
That's why I said we can keep the keywords preserved in the value, with recommended defaults that they map to, but allow browsers to adjust them as they see fit, as long as they stick to the basic pattern. If we never actually expose the timeout behavior of the keywords, authors can only depend on the general behavior, and rough timeouts are hard to hang things on. (And we have things like the Font Loading API now, so you don't have to do hacky racy stuff.)
I'd rather developers think about this (not just resort to a keyword) and apply meaningful timeouts for their particular use case...
This is where I strongly disagree. Beyond these three scenarios, I think most developers shouldn't need to think about it. Most people's use-cases just aren't that complicated, and putting too many requirements on people just results in them ignoring you and copy-pasting something from a tutorial anyway. If you give really easy defaults that are simple to understand, there's a higher chance they'll at least get in the right ballpark with things.
Along those lines... If we drop "swap", and we don't want to promote "mandatory" (which I agree with), we're only left with "optional". Perhaps we can skip the keywords altogether?
We shouldn't drop any of these. While I don't want to promote mandatory, it does have a place.
That's why I said we can keep the keywords preserved in the value, with recommended defaults that they map to, but allow browsers to adjust them as they see fit, as long as they stick to the basic pattern.
I'm not sure I understand what you mean by "keywords preserved in the value"? Based on the discussion on www-style, my understanding was that some UA behaviors (FF, in particular), were not possible to describe with block + swap.. which meant that we needed a more general "auto" concept?
Here's the full list of possible scenarios:
0s 0s: first font wins.0s 1s+: render immediately, allow swap for up to X seconds. Failed req is a noop.0s infinite: render immediately, allow swap indefinitely. Failed req is a noop.1s+ 0s: block for X seconds, use fallback if timeout or request failed.1s+ 1s+: block for X seconds, allow swap for Y seconds; use fallback if X times out or req fails.1s+ infinite: block for X seconds, allow swap indefinitely; use fallback if X times out or req fails.infinite 0s: block until desired font is available, use fallback if request failed.infinite 1s+: block until desired font is available, use fallback if request failed (same as above).infinite infinite: block until font request completes, do not use fallback.
I'd nominate: 1 = optional, 3 = swap, 9 = mandatory.