System Font Stack

Avatar of Geoff Graham
Geoff Graham on (Updated on )

Defaulting to the system font of a particular operating system can boost performance because the browser doesn’t have to download any font files, it’s using one it already had. That’s true of any “web safe” font, though. The beauty of “system” fonts is that it matches what the current OS uses, so they can be a comfortable look.

What are those system fonts? At the time of this writing, it breaks down as follows:

OSVersionSystem Font
macOSMontereySan Francisco Pro (variable)
macOSEl CapitanSan Francisco
macOSYosemiteHelvetica Neue
macOSMavericksLucida Grande
WindowsVistaSegoe UI
WindowsXPTahoma
Windows3.1 to MEMicrosoft Sans Serif
AndroidIce Cream Sandwich (4.0)+Roboto
AndroidCupcake (1.5) to Honeycomb (3.2.6)Droid Sans
UbuntuAll versionsUbuntu

Get to the snippet, already!

The reason for the preface is that it shows how deep you may need to go back to support system fonts. Additionally, it helps show that with new system versions, come new fonts, and thus the possibility of needing to update your font stack.

Method 1: System Fonts at the Element Level

Chrome and Safari have recently shipped “system-ui” which is a generic font family that can be used in place of “-apple-system” and “BlinkMacSystemFont” in the following examples. Hat tip to J.J. for the info.

One method for applying system fonts is by directly calling them on an element using the font-family property.

GitHub uses this method on their site, applying system fonts on the body element:

/* System Fonts as used by GitHub */
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}

Both Medium and the WordPress admin use a similar approach, with a slight variation, most notably support for Oxygen Sans (created for the GNU+Linux operating system) and Cantarell (created for the GNOME operating system). This snippet also drops support for certain types of emoji and symbols:

/* System Fonts as used by Medium and WordPress */
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue",sans-serif;
}

More recently, Chrome and Safari shipped a system-ui, which is a generic font family that can replace -apple-system and BlinkMacSystemFont. That means the GitHub snippet could be reduced to this:

/* System Fonts with system-ui */
body {
  font-family: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}

Note: Booking.com published a thorough write-up that warns against using the snippets that start with -apple-system on the font shorthand property because some browsers may view the leading font as a vendor prefix that will be ignored. Use the font-family property instead, or replace -apple-system and BlinkMacSystemFont with system-ui.

The obvious limitation here is that you have to call that long list of fonts each time you want to apply it to an individual element. Over time, that could become cumbersome and make your code more bloated than it ought to be. Instead, you might consider making a CSS variable for it:

:root {
  --system-ui: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}

.element {
  font-family: var(--system-ui);
}

This is certainly easier to read but is also more maintainable if the stack ever needs updating. Change it once and it applies everwhere!

Method 2: System Font Stacks

Jonathan Neal offers an alternative method where system fonts are declared using @font-face.

/* Define the "system" font family */
@font-face {
  font-family: system-ui;
  font-style: normal;
  font-weight: 300;
  src: local(".SFNSText-Light"), local(".HelveticaNeueDeskInterface-Light"), local(".LucidaGrandeUI"), local("Ubuntu Light"), local("Segoe UI Light"), local("Roboto-Light"), local("DroidSans"), local("Tahoma");
}

/* Now, let's apply it on an element */
body {
  font-family: "system-ui";
}

The snippet has not been updated in some time as of this writing and might result in different fonts, but the reason we’re including it here is that it illustrates the capability to define variations of the system-ui font family that is defined in the snippet above to account for italics, bolding, and additional weights.

If we didn’t have CSS variables, this would be a much more effciient way to write and update the code.