BattlefyBlogHistoryOpen menu
Close menuHistory

The strangely difficult problem of drawing a box around text

Ronald ChenSeptember 14th 2021

Let me show you a quick demo.

Text “BEHOLD, TEXT INSIDE A BOX” inside a grey dotted box tightly surrounding the text. Width and height in pixels are above and to the right of the box.

Not impressed? Its just text inside a <span> with border: dashed 2px gray; right? Well, let’s test that theory. Here’s what that would look like.

Text “BEHOLD, TEXT INSIDE A BOX” inside a grey dotted box loosely surrounding the text.

What’s with the extra white space above and below the text? I must be tricking you by including some padding right? Nope. padding: 0;

What you are seeing here is how CSS does font rendering. People smarter than me have described this in amazing detail.

A few consequences with how CSS does its thing

  • You don’t know how wide text will be until its already rendered and you measure the containing box
  • Two different fonts at the same font-size could have varying amounts of space above and below it. This is why things like align-items: baseline are required to line up text
  • Designers end up tweaking button text up or down by a few pixels in order to vertically centre it perfectly

What’s the solution to all this madness? Here’s where my demo comes in. I’ve implemented a few approaches to calculate the width and cap height (the distance from the baseline to the top of a “normal” capital letter).

I didn’t come up with any novel approaches. These are all existing methods and tools. What I’ve done is to simply build a demo that implements these approaches so you can play around with it and look at the code.

Try the demo yourself

Calculating text width

TextMetrics is a <canvas> API that allows you to get various metrics from arbitrary text rendered on <canvas>. The browser compatibility table is a pretty funny sight. The only thing that isn’t experimental is textMetrics.width, which fortunately exactly what we need.

The MDN documentation also describes a supposedly more accurate way to calculate text width using experimental fields on TextMetrics. They give the formula

Math.abs(textMetrics.actualBoundingBoxLeft) + Math.abs(textMetrics.actualBoundingBoxRight)

But oddly, I found this to sometimes return a width larger than the simple textMetrics.width. I don’t know why this is.

I’ve implemented both of these methods.

Calculating text height?

Well, I didn’t actually calculate the text height. Its more like I implemented two styles of rendering text within the same height.

The first one is just the normal font-size with all the gratuitous space above and below the text.

The other method is uses the Capsize library, which takes in font metrics and produces CSS styling that adjusts the font to render without the extra space.

Design matters

Why am I talking about this esoteric text rendering issue? What does this even have to do with Battlefy?

It frankly doesn’t. We don’t encounter these kinds of issues in our day to day lives, but we understand design matters. We are designing things all the time, and not just user flows, UIs, or technical designs. We are designing tournament formats, compelling esport experiences, domain models, even a new company processes.

Design is the one common skill that unites producers, product managers, designers, and software developers. We can help grow each other by sharing things we learn in our own specialty and translating them into design in general.

I studied how CSS renders fonts because it develops my design thinking. It teaches me how design problems were solved at a time when fonts were literally made of metal blocks. The next time somebody complains about paragraphs having inconsistent spacing when the font changes, I’ll know why and how to fix it.

Do you want to exercise your design skill everyday? You’re in luck, Battlefy is hiring.


Powered by