Not so long ago, we wrote about dark mode in CSS and I’ve been thinking about how white text on a black background is pretty much always harder to read than black text on a white background. After thinking about this for a while, I realized that we can fix that problem by making the text thinner in dark mode using variable fonts!
Here’s an example of the problem where I’m using the typeface Yanone Kaffeesatz from Google Fonts. Notice that the section with white text on a black background looks heavier than the section with black text on a white background.
Oddly enough, these two bits of text are actually using the same font-weight
value of 400
. But to my eye, the white text looks extra bold on a black background.
Stare at this example for a while. This is just how white text looks on a darker background; it’s how our eyes perceive shapes and color. And this might not be a big issue in some cases but reading light text on a dark background is always way more difficult for readers. And if we don’t take care designing text in a dark mode context, then it can feel as if the text is vibrating as we read it.
How do we fix this?
Well, this is where variable fonts come in! We can use a lighter font weight to make the text easier to read whenever dark mode is active:
body {
font-weight: 400;
}
@media (prefers-color-scheme: dark) {
body {
font-weight: 350;
}
}
Here’s how that looks with this new example:
This is better! The two variants now look a lot more balanced to me.
Again, it’s only a small difference, but all great designs consist of micro adjustments like this. And I reckon that, if you’re already using variable fonts and loading all these weights, then you should definitely adjust the text so it’s easier to read.
This effect is easier to spot if we compare the differences between longer paragraphs of text. Here we go, this time in Literata:
Notice that the text on the right feels bolder, but it just isn’t. It’s simply an optical allusion — both examples above have a font-weight of 500.
So to fix this issue we can do the same as the example above:
body {
font-weight: 500;
}
@media (prefers-color-scheme: dark) {
body {
font-weight: 400;
}
}
Again, it’s a slight change but it’s important because at these sizes every typographic improvement we make helps the reading experience.
Oh and here’s a quick Google fonts tip!
Google Fonts lets you add a font to your website by adding a <link>
in the <head>
of the document, like this:
<head>
<link href="https://fonts.googleapis.com/css2?family=Rosario:wght@515&display=swap" rel="stylesheet">
</head>
That’s using the Rosario typeface and adding a font-weight of 515
— that’s the bit in the code above that says wght@515
. Even if this happens to be a variable font, only this font weight will be downloaded. If we try to do something like this though…
body {
font-weight: 400;
}
…nothing will happen! In fact, the font won’t load at all. Instead, we need to declare which range of font-weight values we want by doing the following:
<link href="https://fonts.googleapis.com/css2?family=Yanone+Kaffeesatz:[email protected]&display=swap" rel="stylesheet">
This @300..500
bit in the code above is what tells Google Fonts to download a font file with all the weights between 300
and 500
. Alternatively, adding a ;
between each weight will then only download weights 300
and 500
– so, for example, you can’t pick weight 301
:
<link href="https://fonts.googleapis.com/css2?family=Yanone+Kaffeesatz:wght@300;500&display=swap" rel="stylesheet">
It took me a few minutes to figure out what was going wrong and why the font wasn’t loading at all, so hopefully the Google Fonts team can make that a bit clearer with the embed codes in the future. Perhaps there should be an option or a toggle somewhere to select a range or specific weights (or maybe I just didn’t see it).
Either way, I think all this is why variable fonts can be so gosh darn helpful; they allow us to adjust text in ways that we’ve never been able to do before. So, yay for variable fonts!
For variable fonts to be ideally usable (especially for something like “dark mode”) they should support uniwdith: where a given character is the same set-width (taking up the same space) no matter the weight, etc. Otherwise text will reflow.
https://www.fontshop.com/people/david-sudweeks/fontlists/uniwidth-typefaces
This error is only for variable fonts, right? Didn’t antialiasing work?
I think that’s negligible. Dark Mode is not designed to constantly switched, so the text ideally reflows exactly once. That’s not often enough to reject a font that doesn’t support uniwidth setting.
Some variable fonts have a grade (GRAD) axis; you could decrease the grade without the danger of the text reflowing when coding for dark mode. Google mentions Apple’s system font San Francisco in macOS Catalina supports the GRAD axis for example.
How to change from light to dark mode?
You can change your OS’ theming settings to use the dark theme (on Windows 10, macOS and Linux). If you use Firefox, you can also install the Dark Website Forcer add-on: https://addons.mozilla.org/en-US/firefox/addon/dark-mode-website-switcher/
Definitely looks better to read. Thanks for the advice.
I thought a variable font was one file that included all weights (assuming customizable weight is something that font supports), so why does Google fonts only load some weights?
Google Fonts is introducing a new syntax with the query params. A variable font is returned when a range is defined for an axis (wght in this article’s example). Otherwise, a static font is returned for each individual weight. This article offers a bit more clarification.
Strangely, the 350-weight dark-mode version of “She walked…” is wider than the 400-weight normal-mode version, even though the dark-mode version is lower weight, in the codepen prefixed by “Here’s how that looks with this new example:”. I am using latest Chrome on a Mac.
In font design you try to get an even “color” (perceived density). Because in a lighter font you have more space inside the letters you often add some space between the letters. So lighter fonts can be wider than bolder fonts, because the latter can be spaced somewhat tighter. That’s not generally the case, but often when the type designer doesn’t want bold letters be too wide and light ones too narrow.
This is 100% due to macOS subpixel antialiasing. Applying
-webkit-font-smoothing: antialiased
to all the examples above fixes the problem completely. This is not “just how our eyes perceive shapes and color”, this is a concrete artifact of the computers we’re all using. You will find articles telling you that you should not tinker with-webkit-font-smoothing
and they are all wrong.Also, the latter examples above cheat, by putting #222 text on #fff for the light mode example, and #fff text on #111 for the dark mode example. Shame!
Well, I knew when I wrote that that “100%” was hyperbole. But I wasn’t expecting to be convinced that the second body text example really was noticeably superior after I fixed the colors, turned off subpixel antialiasing, and toggled between screenshots of the examples in Photoshop. Sorry for popping off with snark. I do think the Mac’s default antialiasing greatly exacerbates this issue, though, and that it should be disabled with prejudice. And the colors should be comparable when trying to make a point like this.
Please note, Lanny: In typography it’s well known that luminous letters on dark backgrounds tend to look bolder. Unfortunately I know the technical term only in German – „Überstrahlung“, which could be translated as blooming or flare. If somebody knows the English term, please comment!
I found a good explanation of this effect in an German article about legibility on computer screens. My translation of the relevant part: “Bright areas outshine (flare/bloom into) darker ones, so that a bright background eats away dark letters from the outside and the font seems to be less bold – vice versa bright text seems to be bolder on dark backgrounds.”
Besides on computer screens this effect occurs on luminous or reflective road signage, too (no macOS subpixels here ☺). For that reason specialized fonts even have a slightly thinner version for inverted contexts, e.g. Wayfinding Sans, see https://fdi-type.de/faq/font-usage/what-are-the-positive-and-negative-styles-of-wayfinding-sans-pro-for-r2/
-webkit-font-smoothing: antialiased
helps to fight this Überstrahlung according to https://usabilitypost.com/2012/11/05/stop-fixing-font-smoothing/, as does lowering the overall-contrast. It’s also helpful to slightly (!) letter-space white text on black backgrounds.As always in typography there’s more than one possibility to compensate the numerous subtle optical phenomenons.
I ♥️ the term ‘Überstrahlung’!