OVER THE FULLERENESHIFT
17/11/2024 - The Most Cross-Compatible Font Stacks
Cross compatibility is very important to me; it means consistency, and consistency and permanence are some of the most important things to me in web development, and overall in life.
Sans-Serif and Serif
Loading fonts sucks - they loads slowly and some older browsers don't support it. I was looking for what default system fonts are most cross-compatible and came across this wonderful article. I recommend reading it first. According to it, the most cross-compatible font stack for sans-serif fonts is 'Helvetica Neue', 'Arial Nova', Helvetica, Arial, sans-serif
and for serif fonts, Times, 'Times New Roman', serif
. While this is a great starting point, this isn't exactly what I'm looking for.
When looking for a cross-compatible font stack, I'm looking for one where the fonts are metrically compatible. My first problem with the font stacks given in the Aleksandersen's article is that Helvetica Neue is not metrically compatible with Helvetica. While this doesn't mean something is wrong with using Helvetica Neue to replace Helvetica, it's not exactly what I'm looking for. So, our new font stack for sans-serif fonts is: Helvetica, Arial, sans-serif
.
According to Aleksandersen's article, it's better to first put Times New Roman, then Times in the font stack because Nimbus Roman is a closer match than Liberation Serif. However, looking at the charts provided, Times New Roman is better substituted, and Nimbus Roman isn't metrically compatible with Times, which is what I'm looking for. A solution for this is simply making the font stack like this: "Liberation Serif", "Times New Roman", Times, serif
. The same goes for the sans-serif fonts; "Liberation Sans", Arial, Helvetica, sans-serif
.
Font Ligatures
Another issue with all of this is font ligatures - many metrically compatible fonts don't have the same support for ligatures, but as it turns out, it's possible to disable font ligatures with CSS. Is it worth it? To most people, no, but for my personal needs, yes. Font ligatures can be disabled in two ways. Using font-feature-settings: "liga" 0
or font-variant-ligatures: none
.
There are a few problems with these options however; the first is that it's unsupported by older browsers, but there isn't anything we can do about that. However more importantly, font-feature-settings: "liga" 0
only disables common ligatures; font-feature-settings: "liga" 0
is the same as font-variant-ligatures: no-common-ligatures
but not font-variant-ligatures: none
. So of course font-variant-ligatures
is the better option? Kind of. Official support for font-variant-ligatures
came before font-feature-settings
in most browsers, but font-feature-settings
came with vendored prefix support before font-variant-ligatures
did for most browsers. The solution? Use both, and with all vendor prefixes.
font-feature-settings: "liga" 0;
-moz-font-feature-settings: "liga=0";
-moz-font-feature-settings: "liga" 0;
-webkit-font-feature-settings: "liga" 0;
font-variant-ligatures: none;
-webkit-font-variant-ligatures: none;
That's a lot just to disable font ligatures, but in my eyes it's worth it. Older versions of font-feature-settings
on FireFox had a different syntax, which is why the line -moz-font-feature-settings: "liga=0";
is needed.
Remaining Fonts
Is that it? No. The one final thing not addressed is the most cross-compatible monospaced font. This isn't really complicated, and follows the same pattern as all the other fonts. Using the archlinux wiki, I got the font stack "Liberation Mono", "Courier New", Courier, monospace
.
The generic values for font-family
also include system-ui
, cursive
, fantasy
, and math
. However, system-ui
is dependant on your operating system, cursive
defaults to Comic Sans on Windows, which isn't even cursive and more importantly I doubt anyone has made any metrically compatible fonts to it that are cursive. I can't seem to find what the default font for cursive
is on MacOS anyways, so I'm just not gonna do it. fantasy
on Windows defaults to Arial for some reason, and math
to Times New Roman, so it makes sense to just support PostScript Level 1 core fonts, since those are the only ones standardized. Technically Symbol is also included in PostScript Level 1 but that can't really be substituted.
TL;DR
.serif {
font-feature-settings: "liga" 0;
"liga=0";
-moz-font-feature-settings: "liga" 0;
-moz-font-feature-settings: "liga" 0;
-webkit-font-feature-settings: font-variant-ligatures: none;
none;
-webkit-font-variant-ligatures: font-stretch: normal;
font-variant-caps: normal;
font-variant-numeric: normal;
font-variant-position: normal;
font-variant-alternates: normal;
font-variation-settings: normal;
font-synthesis: style small-caps weight position;
font-family: "Liberation Serif", "Times New Roman", Times, serif;
}.sans-serif {
font-feature-settings: "liga" 0;
"liga=0";
-moz-font-feature-settings: "liga" 0;
-moz-font-feature-settings: "liga" 0;
-webkit-font-feature-settings: font-variant-ligatures: none;
none;
-webkit-font-variant-ligatures: font-stretch: normal;
font-variant-caps: normal;
font-variant-numeric: normal;
font-variant-position: normal;
font-variant-alternates: normal;
font-variation-settings: normal;
font-synthesis: style small-caps weight position;
font-family: "Liberation Sans", Arial, Helvetica, sans-serif;
}.monospace {
font-feature-settings: "liga" 0;
"liga=0";
-moz-font-feature-settings: "liga" 0;
-moz-font-feature-settings: "liga" 0;
-webkit-font-feature-settings: font-variant-ligatures: none;
none;
-webkit-font-variant-ligatures: font-stretch: normal;
font-variant-caps: normal;
font-variant-numeric: normal;
font-variant-position: normal;
font-variant-alternates: normal;
font-variation-settings: normal;
font-synthesis: style small-caps weight position;
font-family: "Liberation Mono", "Courier New", Courier, monospace;
}
It's probably better to use mixins and a CSS preprocessor rather than classes, though. I also added a few more modern features to make sure it's entirely the same, although I'm not entirely certain if they have any effect on the specific fonts in this context. You can probably remove everything from font-stretch: normal;
to font-synthesis: style small-caps weight position;
without losing much.
Basically all modern browsers allow you to override all website fonts so this isn't really a guarantee of cross-compatibility, but nothing can really be done about that. It is what it is. Basically the only solution to that is using images for fonts but that's terrible for accessibility and performance too - unless you're using HTML5 canvas, where there's probably not a lot of text anyways. I might experiment with that later, especially since old Chrome versions don't support writing text directly to canvas.
Thank you for reading!