Crafting link underlines on Medium

How hard could it be to draw a horizontal line on the screen? It seems wrangling a few pixels together to stand in a file would be something computers should be pretty good at anno domini twenty-fourteen.

One would think so, but simple things are rarely simple under the surface… at least if they are worth anything. Typography, likewise, is a game of nuance. This is a story on how a quick evening project to fix the appearance of underlined Medium links turned into a month-long endeavour.

The history

Typography was never particularly fond of underlining. Do you want to emphasize your words? Grab some italics or use bold — hell, add some more tracking in between the letters if you have none of the above. Don’t just draw a line underneath like a cavedouchebag.

Underlines and annotations in a book from 1493. Photo by Penn Provenance Project.

However, we also just described underlines’ most attractive property: it’s very easy to add them to already existing text. That makes it easy to understand why underlines were frequently used as annotations:

  • Drawing underlines, by hand, on top of already printed text — your first experience of the underline might very well have been your teacher scoffing at your bad writing in the primary school.
  • In the typewriter universe of the 20th century — with their inflexible, monospaced fonts, and typically one-coloured ribbons — underline was the only realistic method of highlighting: just backspace through what you already typed, and add a batch of underscores in the same place. (The underscores eventually migrated to the programming world to stand in for spaces in filenames or variable names… with their low placement still betraying the original typewriter roots.)
Typewriter punctuation; see underscore above the number 6

But then came the web, and underlines teamed up with Colour Blue to find a more worthwhile destiny: signifying clickable links. They might disappear one day — even Google abandoned them recently on their search pages — but I don’t believe that’ll happen any time soon.

Unfortunately, for all the advances in web typography we’ve seen during the years — better CSS properties, more support for internationalization, custom web fonts — underlines remained mostly as they were, with very little customization available to web designers.

The project starts tonight

And then things got even worse. I woke up one February morning, and saw Chrome doing this to Medium links:

Talk about the obesity epidemic! Ugly. Distracting. Unacceptable. I looked at the other browsers and while the underlines looked better there, oftentimes the improvements went only halfway. Worse, each browser rendered the underlines as they saw fit (with Firefox faring best of all):

Soon after joining Medium, I started a running document listing all the improvements to typography I wanted us to tackle in the coming months. On the day that Chrome bug manifested itself, I added the underlines… and put them at the very top as the next thing to work on.

The non-branded hammer

I find a lot of inspiration from computer history, and the creative solutions people before me came up with when dealing with the early, inflexible machines.

I took this photo a few years ago in the IBM 1401 restoration lab at the Computer History Museum in California. It’s a suitcase with all the tools necessary to operate your computer… in the 1950s. Most of the items are official and branded, even. (With one exception: the hammer. IBM claimed it shouldn’t be necessary; the repairmen knew better.)

This was the age where computers were still electromechanical, with card punchers, chain printers, tape readers, and all sorts of peripherals needing your brawn as much as they required your brains — even if your eventual goal was very cerebral: to juggle the invisible ones and zeroes.

Web design always seemed like this, too: finding convoluted, “dirty” solutions to often simple problems, using a very limited set of tools. Because no, there isn’t a simple way to tell your browser to just give you the link underlines you want. But yes, perhaps we can figure out some roundabout way.

To find a solution, however, we need to start by defining the problem.

Finding the perfect underline

The perfect underline should be visible, but unobstrusive — allowing people to realize what’s clickable, but without drawing too much attention to itself. It should be positioned at just the right distance from the text, sitting comfortably behind it for when descenders want to occupy the same space:

So, the ideal technological solution would allow us to:

  • change the width of the line (with additional half-pixel/retina support),
  • change the distance from the text,
  • change the color (even if just to simulate thinner width by using lighter grays instead of black),
  • clear the descenders,
  • (perhaps) have a separate style for visited links.

Storming our collective brains

I thought about it a bit myself, and asked some of the very smart front-end engineers around me. Collectively, this was the list of ideas we came up with:

1. As-is

Option one is always sticking with the default. But default is rarely good enough — and just like the rest of our product, we felt our readers deserved better.

2. Advanced underline CSS properties

CSS standards promise a few interesting properties, among them text-decoration-skip and text-underline-position. However, they’re not supported by most browsers and might not be any time soon.

3. Border or box shadow at the bottom

Using border-bottom to modify underlines counts among the prototypical CSS tricks from the late 1990s. It allows us to customize the colour, but the line is usually too far below the letters. A quick prototype confirmed that — the border-bottom underlines felt they almost fell in between the lines of text:

It’s possible to hack our way through this limitation and raise them up by applying display: inline-block and reducing height or line-height, but this has one deathly constraint — the link is not allowed to break to a new line. That won’t work for a regular body text. (Additionally, border-bottom won’t allow us to use anything smaller than two retina pixels.)

Another idea — applying a carefully crafted inset box-shadow on the text — has somewhat similar limitations.

4. Bespoke underline font

Italic fonts are just slanted, and bold fonts are just thicker, right? No, of course not. Italicized and bolded type comes with its own separate letter shapes, customized and meticulously tweaked to preserve the visual integrity of the font:

If we scoff at faux bold and italic, why can’t we consider a proper separate underline font, with the underline being part of each glyph?

One can dream?

This is a potentially promising, but in our case flawed approach:

  • Serving a separate font would incur a big latency penalty (and font files are already the heaviest part of an initial Medium load).
  • Since the line is “baked into” the font, we cannot really move it or change its width depending on the font size… and the colour changes are prohibited also.
  • Font licensing and serving issues can make it too complicated.
  • Kerning this might be tricky.

5. Drawing with <canvas>

Here at Medium, we’re using <canvas> in some unexpected — to me — places, for example our signature full-bleed, blurred images.

If we want to draw a line, why don’t we just draw a line? HTML <canvas> is, after all, designed for controlling individual pixels. This would allow us to have custom width, colour, even draw around descenders. However, the tricky part is lack of support for knowing when the links break — the tools to measure wrapped text precisely don’t exist or are very costly in JavaScript.

(A version of this idea is also a variant of the above, having a bespoke font with just the underline, and drawing it on top of the actual text… which we have to reject for the same reason.)

6. Background images and gradients

And thus, we arrived at our unintuitive saviour: the background image. On the surface, background images don’t seem to have much to do with underlines. However, they have an interesting property of supporting wrapped text (we use backgrounds already for highlighting notes — click on the speech bubble on your right), and they gained enough powers in the recent years to be extremely flexible.

With modern CSS, background images can be positioned and scaled exactly as needed, including support for retina pixels. And they don’t even need to be separate images requiring additional web fetches: we can provide them inline using data protocol, or — even better — synthesize via gradients (which themselves are incredibly powerful).

So that’s what our underline could conceivably be: a tiny gradient; 1 or 2 pixels tall; horizontally stretched as far as it can go; vertically carefully positioned.

7. Clearing the descenders

Unfortunately, the background image/gradient solution won’t allow us to clear the descenders. My colleague Dustin found a way, and it’s as ingenious as impractical — applying a white CSS text shadow or a text stroke to paint over the underline and simulate a gap between the underline and the text.

Unfortunately, in order to achieve the best effect, we’d need to layer many, many fuzzy text shadows on one another, and that proves to be very expensive. (CSS property text-stroke, which seems to be perfect for this, doesn’t just go outside the text, but also overlaps some of the inside, rendering it thinner.) We might come back to this one day. For now, we had to do what sometimes needs to be done to ship things: mentally relegate this idea from the crucial part of the cake to just its icing.

(Note that this solution would also not work on text that overlays images, since we wouldn’t know exactly what the background colour would be.)

Where things get complicated

Figuring out how to position one underline perfectly and test it on major browsers seemed like a nice, fast evening project. But this was my first month at Medium. I was still learning how complex our product is — and how carefully we hide that complexity from visitors.

Soon, I found out (or was reminded) that:

  • There are five places where our writers can use links: body text, H1, H2, image captions, and pull quotes. (That’s not even counting links in the UI, which I decided were out of scope for this project.)
  • Most of the links will be black on white, but some of them can be drawn on top of images.
  • There are, of course, displays with retina and non-retina pixels.
  • We adjust the sizes of fonts for tablets and mobile phones.
  • We fall back to a default system font for when we decide that our type doesn’t support all the characters in a given language.
  • Some people zoom in (or zoom out) their browsers.
  • Some people use uncommon browsers.
A screenshot from my test Medium story

I carried on, adding more and more complexity and magic numbers to my suggested change. And this is where the code reviewers started asking the truly hard and important questions: Is this too complicated? Will this make the CSS too heavy? How would we maintain it? What happens if this fails?

And so, during the following weeks, I worked on:

  • coming up with formulas rather than arbitrary values — in the event of changing the fonts or font sizes in the future, adjusting the underline positions would be much easier and faster,
  • limiting the browsers we use so that in the case of failure, the reader could see the default browser underlines (rather than not seeing them at all!),
  • simplifying the code so that while we might not get the perfect underlines, we could save both the bytes and future maintenance costs.
Some of my underline calculations

And then, it happens

There’s something anticlimatic about finishing a long code review. There are no fireworks, no champagne; GitHub doesn’t change the UI in any celebratory way to highlight your achievement. After 31 days of discussions, trials and errors, and trying many approaches, both of my great reviewers sent the magic four letters: LGTM.

I am writing this on the public version of Medium which doesn’t yet have the change in it, and I cringe whenever I see the underlines — but also, I am happy that whenever you will read this, they will, for the first time, look really good.

Before and after (Chrome)

There’s another reason long code reviews are anticlimatic: in the meantime, one usually starts and refocuses on newer projects. So much more work still needs to be done, so much work has already begun, and I am already excited for when I can tell you more about the upcoming projects from the typography wishlist.

One of them is improving how… LGTM looks (alongside, of course, all the other initialisms/acronyms). Stay tuned — and link away.

In the Medium typography series, we later covered hanging quotes, Whitespace, and pilcrows. Any typographical comments and suggestions? Send us feedback to

Many thanks to Daryl Koopersmith for inspiration to tackle this project and his guidance along the way.

Further reading or efforts from readers