Site Design Changes
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.