“A nearly impenetrable thicket of geekitude…”

Site Design Changes

Posted on December 28, 2020 at 12:38

I have a long list of changes to make to this site one day, when I get the time or (more plausibly) when I am looking for a distraction.

This year, I have finally addressed the first of these, and if you’re reading this on the site rather than in a feed reader you may notice that most text now appears in the sans-serif font used by your operating system’s user interface. These fonts are usually highly optimised for screen use, and the result in most cases will be an improvement in readability, particularly on smaller devices.

This change sounds simple (and in the end, it was very straightforward) but required a lot of behind-the-scenes work to get to the point where it was simple to do.

If you’re interested, read on for details.

Under the Covers

This site has looked more or less the same since early 2012, when I converted it to Drupal. In 2018, I converted to Nanoc but made a point of keeping the appearance the same down to the pixel level to avoid having to make any design decisions.

The downside of that approach to the conversion was that I was left with a lot of the artifacts of a Drupal site, without any explicit mechanism to examine. For example, in Nanoced I mentioned:

… the HTML hierarchy for this text will be something like this: <html>, <body>, <div>, <div>, <div>, <div>, <div>, <div>, <div>, <div>, <div>, <div>, <div>, <div>, <div>, <div>, <p>. That is fourteen levels of <div>.

Making things worse is the fact that each of those elements were given id and class attributes that might be targeted by CSS selectors for styling. Font sizing was achieved solely by applying multipliers at several of those levels, meaning that the resulting size of text might be affected by many CSS rules, and that a change to any rule had side-effects in many places, most unintended.

This scheme was essentially unmaintainable: changing the size of one element in isolation always needed several rules to be changed to balance the change out elsewhere.

One angle of attack on this situation involved understanding and then refactoring the system of classes applied to the element hierarchy, and then removing redundant structure. I’ve used the principles described in SMACSS when building the new structure, and as well as untangling different parts of the site from each other, one result has been that the HTML hierarchy for this text has come down from fourteen to “only” nine levels of <div>. I’m very sure that can be reduced further with more work, but it doesn’t seem like a good use of time now that I’ve reached the result I wanted.

Another source of inconsistency in the original Drupal styles was the repeated use of magic constants for dimensions, font families and font sizes. A lot of that is now handled behind the scenes using Sass, a CSS preprocessor and extension language that Nanoc has built-in support for.

Finally, a lot of the “epicyclic” nature of the Drupal styles was caused, as mentioned above, by the use of compounding font size multipliers (e.g., font-size: 1.071em; at one level and then font-size: 0.933em; later). This was a problem for everyone, not just Drupal, and as a result some time around 2013 browsers started incorporating support for “root em” units. This allowed font-size: 1.071rem (note the r) to mean “1.071 times the font size computed for the root element (i.e., the <html> element)” no matter where it appears in the HTML hierarchy, but without affecting the user’s ability to set that root element font size according to their needs.

Combining Sass with root em units allows this kind of simplification:

@mixin font-size-rem($wanted) {
  font-size: 1rem * ($wanted / $font-size-root);
}

h2 {
  @include font-size-rem(22px);
}

.node-full {
  background: none;
  border: none;
  padding: 0;

  > .content {
    @include font-size-rem($font-size-normal);
  }

}

Font Stacks

Finally, we come to the main event: which fonts should we use?

The Drupal styles set almost everything like this:

font-family: Georgia, "Times New Roman", Times, serif;

This was repeated (usually but not always consistently) in many places across the stylesheets. Fixing that inconsistency with Sass was just a matter of defining a variable $serif (and corresponding variables for $sans and $mono):

$serif: Georgia, "Times New Roman", Times, serif;

font-family: $serif;

This remains my serif font stack, although it’s now only used in a couple of places (the site slogan in the header being one). I’m not a huge fan of Georgia, in fact, but it’s one of the few serif typefaces widely available to browsers and actually designed for screen use. It’s a lot better than Times New Roman, in particular, which was intended for print and has too much contrast between its “thick” and “thin” strokes to work well on screen.

Of course the big change in this site has been its switch from being predominantly set using a serif font stack to almost exclusive use of a sans-serif stack. This makes the choice of the $sans stack critical. I’m not a designer, so I effectively punted the hard work here to the modern generation of technical sites (such as GitHub and GitLab) who have designed sans-serif and monospaced font stacks that pass the buck back to your operating system.

Before this change the sans-serif stack on this site looked like this:

$sans:  "Helvetica Neue", Helvetica, Arial, sans-serif;

This means, roughly, “If you’re on a Mac, use the revised Helvetica from 1983, but fall back to the 1957 version, or knockoffs, if you can’t get that”. It’s not a bad font stack, really, but none of the typefaces involved (even Arial, from 1982) were really designed with modern high-resolution displays in mind. We can do better.

GitHub and GitLab, and now this site, use some variant of this instead:

$sans: -apple-system, BlinkMacSystemFont, "Segoe UI",
  Helvetica, Arial, sans-serif,
  "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";

You can read more about the evolution of this semi-standard at Shipping system fonts to GitHub.com by Mark Dotto, but the executive summary might be:

  • -apple-system: If you’re using Safari on a Mac, use the system’s default user interface font.
  • BlinkMacSystemFont: If you’re using Chrome on a Mac, use the system’s default user interface font.
  • Segoe UI: If you’re on Windows, use the system’s default user interface font.
  • Helvetica, Arial, sans-serif: Oh dear, we have to use Helvetica or some knock-off of it because we don’t have access to the UI font.
  • Let’s put in some fonts to handle emoji, which we understand the young people like to use.

I initially found it interesting that this font stack didn’t include system-ui instead of or as well as -apple-system and BlinkMacSystemFont; it’s intended to be the same thing and has more or less the same browser coverage. This seems to be because it doesn’t work properly (at least right now) with some non-English languages. Perhaps this can be simplified in a couple of years.

Finally, let’s look at monospaced text, which is mostly used on this site for code samples like the ones above and embedded <code>. Here’s the old stack:

$mono:  Menlo, Consolas, "Andale Mono", "Lucida Console", "Nimbus Mono L",
        "DejaVu Sans Mono", monospace, "Courier New";

Ignoring the meaningless "Courier New" coming after the generic monospace identifier, this is just a list of various not-bad monospaced fonts that are sometimes installed on various systems: Menlo is shipped with Macs, Consolas is shipped with Windows but is widely installed elsewhere if you’ve ever installed Microsoft software, the rest are things your particular flavour of Linux might have given you.

Note that there’s a lack here of any kind of cross-platform standardisation or compatibility.

This is the modern equivalent:

$mono:  "SFMono-Regular", Consolas, "Liberation Mono", Menlo,
  Courier, monospace;

This is interesting because it ought to be problematic but turns out to work just fine.

"SFMono-Regular" is an attempt to reach a monospace variant of the San Francisco font family shipped with modern versions of macOS. After all, it would be nice to be on a Mac and see the standard system monospace typeface alongside the corresponding sans-serif one.

I suppose this worked for a while; it no longer does. In fact, if you’re on a Mac running Safari 12 or later, none of "SFMono-Regular", Consolas or "Liberation Mono" will ever select the font family to use even if you have those families installed. So, you’ll get Menlo as before, and like it (it’s actually fine). If you use Chrome or Firefox on the same machine, you’ll see Consolas instead, as you’d perhaps expect.

This behaviour seems to be due to Safari’s extremely strict protection against browser fingerprinting: it will not admit to supporting any local font other than those the base OS was shipped with, even if the attacker resorts to canvas fingerprinting. This means that the fonts you’ve installed yourself can’t be used to distinguish you from other users of the same version of the OS.

The documentation for Tracking Prevention in WebKit elaborates like this:

Changed font availability to web content to only include web fonts and fonts that come with the operating system, but not locally user-installed fonts. Web fonts and the common set of web-safe fonts, as well as other OS-bundled fonts, are still available.

This is why we can’t have nice things.

It does look as if there is some chance that this might be addressed by new font family aliases such as ui-monospace. At the moment these seem to be specific to Safari (on macOS, iOS and iPadOS) but in those environments ui-monospace does seem to give the typeface I want. ui-serif might also be worth adding to the $serif stack: it would really be nice to have all the fonts used look like they were designed together when that’s possible.

Tags: