
Implementing Font-Size in Servo: An Unexpectedly Complex CSS Property - kibwen
https://manishearth.github.io/blog/2017/08/10/font-size-an-unexpectedly-complex-css-property/
======
tannhaeuser
What an amazing article. Thanks for sharing.

I've been wondering for some time now whether CSS should have a formal
semantics to help implementations, and for posteriority (eg. not leaving
behind a mess of specs making it infeasible to implement browsers from
scratch/specs for generations to come).

For an example of what I'm after, see [1], which is using attribute
grammars/logic grammars for a small fragment of CSS box layout, and is one of
the precious few attempts of a formal semantics for CSS.

[1]:
[https://lmeyerov.github.io/projects/pbrowser/pubfiles/extend...](https://lmeyerov.github.io/projects/pbrowser/pubfiles/extended.pdf)

~~~
thinkMOAR
The above, plus i did not know about 'rem'. And now i do, thanks for that.

~~~
Manishearth
There are a bunch more interesting ones that folks often haven't heard of,
like ex (relative to the height of an x) or ch (relative to the width of a 0)

[https://developer.mozilla.org/en-
US/docs/Web/CSS/length](https://developer.mozilla.org/en-
US/docs/Web/CSS/length)

(not all of the ones in that page are implemented by browsers, e.g. I don't
think anyone implements `cap` or `ic` since it's from a draft spec)

------
Illniyar
These kind of complexities is why, as much as I would love it to, the web as
it is cannot compete with the performance of native apps.

What is in native a simple number (maybe two) is dozens of pages of
specifications - which result in insane amount of work to deduce the value of
a simple variable.

Layout and rendering is probably even worse in this regard.

~~~
pcwalton
On the contrary, when native apps support styling (e.g. through React Native),
it's invariably very slow compared to all the optimizations that have gone
into browsers. They generally can't compete with the decades of optimization
that have gone into the Web platform.

Sure, cascading of certain properties can be complex. But the compressed in-
memory style representation, style sharing optimizations, parallel restyling,
etc. more than make up for this. Each one of these is at least a 2x speedup on
its own. Every single implementation of native app styling that I've seen
hasn't even begun to implement _any_ of these optimizations.

~~~
vanderZwan
I don't know if you're aware of this, but you're making a different comparison
than GP:

GP: a simple way to set font size, say, in pixels vs CSS styling

You: native support for CSS styling vs browser CSS styling

Of course browsers are faster with the latter, due to the reasons you
mentioned. But they cannot hope to be able to optimise as much and as easily
as the former approach (in principle that is, I cannot say anything about
whether or not they actually do).

So I'd say you're both right by virtue of not debating the same thing.

PS: When are the fast SVGs demo'd in Servo a long time ago coming to Firefox?
:)

~~~
pcwalton
Setting font size in pixels on an inline style is as fast on the Web as, say,
changing the font on an NSTextFieldCell. Why wouldn't it be?

~~~
vanderZwan
You're again talking about current performance.

The full HTML and CSS spec is large, complicated, and full of corner cases,
and thus a lot harder to optimise. The point GP is making is that a simpler
framework is much easier to maintain and optimise because it has much less to
worry about (again, _in principle_ ).

I think the key difference is this part of your earlier reply:

> _when native apps support styling_

.. when the implicit statement Illnyar was comparing CSS to a framework that
_doesn 't support styling_, or at least much simpler styling.

I'm not hating on browsers, HTML or CSS here - they're amazing technologies.
But these are two different points being made.

~~~
pcwalton
Can you explain, specifically, how the code path for setting a specific
numeric font size via inline style is slower than, say, setting the same
attribute on an NSMutableAttributedString? (I know of one way in which it is
slower, but it has nothing to do with anything discussed in this article and
is fixable via work-in-progress specs.)

With native apps, you can either use the raw style modification methods or a
comparatively slow third-party style system of your choosing. With the Web,
you can either use inline styles (akin to the raw style modification) or a
slower, but still relatively fast style system. I don't see any advantage for
native apps here.

~~~
vanderZwan
Why do you keep arguing something that I'm not even disagreeing with? And why
are you not even engaging with my earlier statement that you're arguing a
different thing altogether. If I'm wrong about that, then just explain to me
why instead of trying to convince me of something I'm already agreeing with.

I mean, you're the expert working on Servo here, so you're better equipped to
answer the other claim that you're ignoring: is a simpler spec than HTML and
CSS be easier to optimise, yes or no?

~~~
Manishearth
> If I'm wrong about that

You are wrong about that, and he did explain. He is talking about the same
thing. That was the purpose of his comment about inline styles. (If it was
unclear I can answer any more specific questions you have)

What native frameworks let you do is assign inline styling to mostly leaf
nodes, plus some extremely generic overall theming functionality.

This is both possible and _fast_ in CSS. This is not where CSS is complicated.

Comparing apples to apples, they're simple specs.

You're saying "But they cannot hope to be able to optimise as much and as
easily as the former approach (in principle that is, I cannot say anything
about whether or not they actually do)." \-- this is incorrect. Setting inline
style on leaf nodes is very fast and not a performance issue especially if you
solely rely on inline style. The main reason I can think it would be a perf
issue is that in CSS this will have to re-parse (whereas in native you can set
these things without string ops), but that is fixable (typed CSSOM is probably
going to happen). But aside from that, it's a simple computation followed by
setting something on a struct. Not really a problem. You can't really optimize
it further, aside from the typed CSSOM thing I mentioned (which has nothing to
do with complexity).

CSS gets complicated when it attempts provides more power than these native
frameworks, however the associated perf issues crop up _when you try to use
these features_. If you use CSS restricted to the feature set of basic native
frameworks, these complexities will not affect you much.

pcwalton made an additional point on top of that that if you want to compare
apples to apples _here_ , you get frameworks with that level of power (e.g.
React Native) on the other side, which don't have as many optimizations.

------
christophilus
I'm always amazed by how performant complex CSS can be. Really complicated
pages render in a few hundred ms, even though so much complicated computation
is happening under the hood. Browser developers are true wizards.

------
leeoniya
> A lot of the web platform has hidden complexities like this, and it's always
> fun to encounter more of them.

"fun" is an odd choice of words here; (╯°□°)╯︵ ┻━┻ seems more apt.

also, what's the future of MathML? what's the point of continuing to support a
standard that Chrome has decided not to implement? it means 50% of web users
won't see MathML, so web authors will never bother writing it.

there was this a while ago:
[https://news.ycombinator.com/item?id=11444830](https://news.ycombinator.com/item?id=11444830)

~~~
Manishearth
Well, from my point of view it's less work to implement it than it is to argue
removal from Gecko (which could happen, but it would take a while). Stylo must
have Gecko parity, whether it's by adding more features to stylo or unshipping
features from Gecko. We've done the latter in some cases for nonstandard stuff
folks no longer use.

I don't really know what the future of MathML is. Some folks seem to still use
it, though less often on web pages.

~~~
leeoniya
Thanks. Also, does Servo incur any overhead from Stylo having to implement
legacy or Gecko-parity features (eg -moz prefixes, etc)? Or does Servo not use
Stylo directly? Same question for other Quantum components.

~~~
Manishearth
Stylo is the project name for taking Servo's CSS system and hooking it up to
Gecko.

We conditionally compile out unsupported stuff. There is some complexity in
shared code introduced but very little of it is anything but a complication of
the abstractions (not an actual behavior-level change which can have a perf
impact).

So for example the weird cascading behavior exists when you compile pure servo
(because that's a core functionality of font-size, which servo does
implement), but servo doesn't have to deal with the mathml stuff or text-zoom.
Servo has some no-op _code_ for text-zoom that lets shared code be written
cleaner, but it compiles out.

------
mynewtb
What an interesting and educational post! Thanks!

It's baffling just how complex CSS became over the years. I wonder, is the
extra work involved for all the relative tracking something to consider for
website performance?

~~~
icebraining
_It 's baffling just how complex CSS became over the years_

Well, except for calc(), these cases seems to come all from CSS 1.

Interestingly, the font-size section mentioned how a font may be sized in a VR
environment. I remember VRML, but didn't know there were plans to apply CSS to
VR back then!

[https://www.w3.org/TR/1999/REC-CSS1-19990111#font-
size](https://www.w3.org/TR/1999/REC-CSS1-19990111#font-size)

~~~
olavk
Even before CSS 1... Initially CSS tried to emulate what was available in
"presentational HTML" available at the time. The absolute and relative font
sizes were introduced by the FONT-element and BASEFONT-element, and the BIG
and SMALL elements.

------
irrlichthn
I wrote a free responsive website editor (named "RocketCake"), and was
surprised how complex CSS and these HTML rules are to implement, although I
only needed not all of them. When I told my fellow programmers, they didn't
believe me. I'll send them a link to this. Nice article!

------
_pmf_
Would one really expect anything at all in CSS to be simple?

------
jdmichal
> What this effectively means is that you need to keep track of two separate
> computed font size values. There’s one value that is used to actually
> determine the font size used for the text, and one value that is used
> whenever the style system needs to know the font-size (e.g. to compute an em
> unit.)

As someone who actually sets minimum font size, and to a healthy 14px at
that... Does anyone know the actual reason behind this? It would probably go a
long way to helping layouts not break all the time with my minimum font.

~~~
Manishearth
I don't, and I'm pretty baffled that it _does_ affect em units for text-zoom
(despite the two having very similar purposes).

Perhaps the difference is that with min font sizes everything gets clamped,
which can make two objects of different sizes get the same size if they used
em units (if em units affect things). Which is both good and bad, depending on
the intent. (Whereas with text-zoom everything is nicely scaled so it's less
of a problem).

~~~
jdmichal
Thanks for the response. Is this behavior something defined in the spec? Or is
it just what Gecko does, and is being carried over to Servo?

~~~
Manishearth
text-zoom isn't a web feature, it's only a feature Firefox/Gecko has.

Servo doesn't implement it either, only Stylo (the confluence of Servo's style
system and Gecko). In Servo mode all the code for text-zoom becomes a no-op.

~~~
jdmichal
Sorry, I meant the em sizing ignoring clamping, not the text zoom!

~~~
Manishearth
Clamping isn't exactly a web feature either, it's one that most browsers have.

Probably should be specced; one thing I intend to do is help spec this.

------
jancsika
> Text zoom is also disabled within <svg:text> elements, which leads to some
> trickiness here.

Not sure I understand this. Is the author saying that if the user tries to
zoom in, svg text won't change size?

~~~
Manishearth
If the user tries to _text zoom_.

Yes, SVG text will not zoom if you text zoom. SVG is fundamentally a
absolutely-position image, not a laid out document. The concept of text-only
zoom makes sense for documents, but not for images, where the position of the
text is far more important.

(This won't affect `<img>` embedded SVG anyway, however it is possible to
embed SVG directly within HTML via `<svg>`, which is where this matters.)

~~~
jancsika
> If the user tries to text zoom.

Ah, thanks for the clarification. I missed that part. That makes absolute
sense. :)

> The concept of text-only zoom makes sense for documents, but not for images,
> where the position of the text is far more important.

> SVG is fundamentally a absolutely-position image, not a laid out document.

svt text is a fundament of svg that is incompatible with absolute-positioning.
The OS's font stack might render px-sized mono-spaced font text at width x, or
it might render it at x+7.

Edit: typo

------
rhythmvs
> “monospace fonts tend to be wider, so the default font size (medium) is
> scaled so that they have similar widths”

> “the base size depends on the font family and the language … Default system
> fonts are often really ugly for non-Latin- using scripts.”

‘often’, ‘tend to be’: I am worried. I think it’s a really bad idea to deflect
default behavior based on such assumptions, certainly when the deviations are
a blind process triggered by proxies (like language tags and some vaguely
statistical rules-of-thumb for dealing with generic font family names). That
is: without even looking at the actual design and metrics of the actual font
involved.¹

What happens if the monospaced font in case has a normal x-height and/or an
advance-width equal to that of its serif counterpart? What if the CJK and
Devanagari fonts have characters drawn already ‘big on the body’?² Then such
hard-coded default moonshot-fixes which try to cater for the lowest common
denominator will make things needlessly hard to debug and force the designer
still to ad-hoc size-adjust font per font, but now also trying to fix the
browser’s ‘fixes’. (Too bad: any `normalize.css` wont help…³)

And yet, all of the needed data is available in the font file. There’s even a
dedicated CSS property for dealing with fonts’ varying metrics: `font-size-
adjust`.⁴ Not that browser makers care to implement⁵, but since the OP’s post
concerns Firefox (which does support `font-size-adjust`, but the article does
not discuss it) I wonder: is it a matter of performance that retrieving the
actual font metadata and metrics is left out of the equation? Surely, the fact
that local font files, base64 data-URI embedded or externally hosted ones can
be used, makes implementation all but trivial…

At Textus.io⁶ we’re going great lengths to solve typographic issues such as
these. Point in case: for each font we read out the `xHeight` value, then
calculate the actual font-size relative to the font’s UPM (unitsPerEm), so we
have consistent apparent font-sizes, c.q. aspect ratios.

I think it all boils down to a separation of concerns: proportion and
interrelated sizes (ascender, caps, descender, x heights, stem width, etc.)
are up to the discretion of the font designer, overall aspect size is the
business of the typesetter (css stylesheet author), and the browser ought
always draw consistently, regardless of generic font family name, language
and/or Unicode code range.

¹ [https://opentype.js.org/font-inspector.html](https://opentype.js.org/font-
inspector.html) ² [https://medium.com/@xtianmiller/your-body-text-is-too-
small-...](https://medium.com/@xtianmiller/your-body-text-is-too-
small-5e02d36dc902) ³
[https://github.com/h5bp/html5-boilerplate/issues/724](https://github.com/h5bp/html5-boilerplate/issues/724)
⁴ [https://developer.mozilla.org/en/docs/Web/CSS/font-size-
adju...](https://developer.mozilla.org/en/docs/Web/CSS/font-size-adjust) ⁵
[http://caniuse.com/#feat=font-size-adjust](http://caniuse.com/#feat=font-
size-adjust) ⁶ [http://www.textus.io](http://www.textus.io)

~~~
ptx
> I am worried ... really bad idea ... will make things needlessly hard to
> debug

It has worked this way since the days of Netscape Navigator[1], though, so
it's not a change or a new idea. It's just keeping things the way they've
always been. (Nowadays, in Firefox, the corresponding settings are under
Preferences -> Content -> Fonts & Colors -> Advanced.)

[1]
[http://www.alanwood.net/unicode/mac_net6.gif](http://www.alanwood.net/unicode/mac_net6.gif)

~~~
hsivonen
That screenshot is from Netscape 6—a Gecko-based Netscape release without
“Navigator” in the name. The mechanism mostly survives in Gecko today except
for the Latin unification (previously split to Western, Central European,
etc.).

~~~
ptx
Oops! It was definitely there in Netscape 4, but I was a bit sloppy with the
image search. Here is the Netscape 4 screenshot:

[http://www.alanwood.net/unicode/mac_net472.gif](http://www.alanwood.net/unicode/mac_net472.gif)

------
nnwright
Fascinating read, thanks for sharing!

------
p0nce
Thanks, I had always wondered how font-size was implemented.

------
LoSboccacc
he's missing out sizing in viewport dimensions, even if is probably reducible
to the pixel dimensioning adjusted for zoom.

~~~
Manishearth
Yeah, since that's just another length unit and works for all length values I
didn't go into it.

------
Rodd45
Interesting to see how complex it has become.

