How To Define Custom Fonts in CSS with @font-face and font-display
Table of Contents
Introduction #
@font-face
is a CSS at-rule used to define custom fonts. With @font-face
, you provide a path to a font file hosted on the same server as your CSS file. The rule has been around for quite some time, but there is a newer property, font-display
, which brings a new level of loading options.
In this tutorial, we will download the popular, open-source font, Roboto Mono, and use @font-face
to load the font on a sample webpage. To create the best user experience, we will then use the font-display
property to customize how and when to load it.
Prerequisites #
A code editor of your choice, such as nano
or Visual Studio Code
A web browser
A comfort with HTML fundamentals. You can view our tutorial series How To Build a Website with HTML for an introduction.
Step 1 — Downloading the Fonts and Building a Web Page #
Before we begin exploring the @font-face
rule, let’s set up a sample web page and directory.
From a working directory, make a new folder for our website and a subdirectory for our font files:
mkdir -p ./website/fonts/
Navigate into our new project’s root directory, website
:
cd website
We will run all remaining commands from here.
Now we will use the curl
command to download the Roboto Mono font. We are using a popular app called google-webfonts-helper
, which allows us to download multiple fonts directly from Google’s Content Delivery Network in a single, neatly bundled GET
request.
Let’s download two different styles and weights of Roboto Mono, regular
and 700italic
:
curl -o ./fonts/fontfiles.zip "https://google-webfonts-helper.herokuapp.com/api/fonts/roboto-mono?download=zip&subsets=latin,latin-ext&variants=regular,700italic&formats=woff,woff2,ttf"
Note how we are specifying the variants
that we want from the Roboto Mono font family. We then specify the formats
that we would like for each. We are requesting the ttf
, woff
, and woff2
formats. The woff2
format is the most modern web font format, but support for woff2
is still lacking in some browsers. Therefore, we are also providing a fallback in woff
format, which has support back to Internet Explorer 9, and a TrueType format, or ttf
. This will give us excellent coverage, but when we write our CSS, we will provide more fallback options using standard fonts. The font-display
property will also help us manage how we load fonts for various users.
Now unzip the downloaded contents to our ./fonts
folder. On machines running Linux and macOS, use the following command:
unzip ./fonts/fontfiles.zip -d ./fonts
Examine the contents of the ./fonts
folder:
ls ./fonts
We now find six new files—a .ttf
, a .woff
, and a .woff2
file per font.
With our fonts downloaded, let’s write some CSS and use it to style an HTML element.
@font-face
Rule>Step 2 — Using the @font-face
Rule
#
In this step, we will apply our downloaded fonts using the @font-face
property.
Using nano
or your preferred text editor, create and open a file called style.css
:
nano style.css
Add the following content, which will define the @font-face
rule with paths to our files:
style.css
@font-face {
font-family: 'Roboto Mono', monospace;
src: url(fonts/roboto-mono-v12-latin-regular.woff2) format('woff2'),
url(fonts/roboto-mono-v12-latin-regular.woff) format('woff'),
url(fonts/roboto-mono-v12-latin-regular.ttf) format('truetype');
}
@font-face {
font-family: 'Roboto Mono', monospace;
src: url(fonts/roboto-mono-v12-latin-700italic.woff2) format('woff2'),
url(fonts/roboto-mono-v12-latin-700italic.woff) format('woff'),
url(fonts/roboto-mono-v12-latin-700italic.ttf) format('truetype');
font-weight: 700;
font-style: italic;
}
h1, p {
font-family: 'Roboto Mono', monospace;
}
h1 {
font-family: 'Roboto Mono', monospace;
font-weight: 700;
font-style: italic;
}
Let’s examine this code block by block.
Always define your @font-face
at-rules first in your main CSS file. The most basic version of the @font-face
rule requires the font-family
and src
properties. In our first block, we provide Roboto Mono
as the value for font-family
, and we provide paths to our three files for src
, each with a different format and in descending order of priority.
In our second block, we provide the same font-family
value, but we use paths to our 700italic
version of Roboto Mono. We then define two properties, font-weight
and font-style
. We will use these properties to define where we want to use this second version of Roboto Mono.
In our next two blocks, we are defining custom styles for <h1>
and <p>
elements. Note how we use the font-family
to define both but then add font-weight
and font-style
to a block specific to <h1>
. This will render our H1
headings in Roboto Mono 700italic
instead of Roboto Mono regular
.
Save and close the file.
Now let’s build a small HTML page with some <h1>
and <p>
tags.
Create and open a new file called index.html
:
nano index.html
Add the following code, which will define a heading and a line of text:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>CSS @font-face</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1> Roboto Mono font, 700italic </h1>
<p> Roboto Mono font, regular </p>
</body>
</html>
Save and close the file.
Load index.html
in a web browser. You will see that your heading is rendered in Roboto Mono 700italic
while your paragraph text is rendered in Roboto Mono regular
, like this:
Roboto Mono font, 700 italic
Roboto Mono font, regular
With our @font-face
property working, let’s now use font-display
to configure how and when the fonts are loaded.
font-display
to Control Font Loading>Step 3 — Using font-display
to Control Font Loading
#
With font-display
, we can control exactly how we want to load our fonts. This can greatly improve the user experience when using custom fonts.
Sometimes, when using custom fonts, a user can encounter either a FOUT (flash of unstyled text) or a FOIT (flash of invisible text) when a page is first loaded. Some browsers choose to show our text right away, even if the custom font is not loaded. The browser will revert to the custom font once it fully loads, but this creates a FOUT. Other browsers will hide the text for a short period until the custom font load, causing a FOIT. If the font doesn’t load during the window of time, the browser will use a fallback font.
One way to deal with FOUTs is to use a tool like Font Style Matcher to find a fallback font that is as close to the custom font as possible so that the font change doesn’t feel so drastic. We can, however, use the font-display
property to handle these issues more elegantly.
To confront loading issues, font-display
takes one of 5 values:
auto
: This uses the browser’s default behavior, which will vary.
block
: The text is first hidden for a short period, but will change to the custom font when it becomes available. This one value is said to have an infinite swap
period.
swap
: The text is never hidden and changes to the custom font when it becomes available. This also provides for an infinite swap period.
fallback
: The text is hidden for a very short period (the block period), then there’s a short swap period. If the custom font doesn’t load within the swap period, then it isn’t loaded at all.
optional
: The text is given a very brief block period to load (~100ms). If the font doesn’t load during that block period, the fallback font is used and the custom font is not loaded at all. However, the font is still downloaded and cached behind the scenes. This means that, on subsequent page loads, the custom font will become available in the cache and then will load instantly.
The optional
value for font-display
provides a robust solution to many font loading situations. Let’s add it to our own CSS.
Reopen style.css
:
nano style.css
Now add the highlighted code:
style.css
@font-face {
font-family: 'Roboto Mono', monospace;
src: url(fonts/roboto-mono-v12-latin-regular.woff2) format('woff2'),
url(fonts/roboto-mono-v12-latin-regular.woff) format('woff'),
url(fonts/roboto-mono-v12-latin-regular.ttf) format('truetype');
font-display: optional;
}
@font-face {
font-family: 'Roboto Mono', monospace;
src: url(fonts/roboto-mono-v12-latin-700italic.woff2) format('woff2'),
url(fonts/roboto-mono-v12-latin-700italic.woff) format('woff'),
url(fonts/roboto-mono-v12-latin-700italic.ttf) format('truetype');
font-weight: 700;
font-style: italic;
font-display: optional;
}
h1, p {
font-family: 'Roboto Mono', monospace;
}
h1 {
font-family: 'Roboto Mono', monospace;
font-weight: 700;
font-style: italic;
}
Now our custom font will either load so quickly that the user will never experience a FOUT or a FOIT, or it won’t load at all. However, it will still download and load instantly upon refreshing or visiting more pages.
Conclusion #
In this tutorial, we downloaded a custom font and used the @font-face
property to add it to a webpage. We then used the font-display
property to manage how the custom font will load, if at all. To learn more about font-display
and related properties, consider exploring the related documentation on the Mozilla Developer Network.
@font-face {
font-family: ‘Roboto Mono’, monospace;
src: url(
https://assets.digitalocean.com/articles/406/roboto-mono-v12-latin-regular.woff2) format(‘woff2’),
url(
https://assets.digitalocean.com/articles/406/roboto-mono-v12-latin-regular.woff) format(‘woff’),
url(
https://assets.digitalocean.com/articles/406/roboto-mono-v12-latin-regular.ttf) format(‘truetype’);
}
@font-face {
font-family: ‘Roboto Mono’, monospace;
src: url(
https://assets.digitalocean.com/articles/406/roboto-mono-v12-latin-700italic.woff2) format(‘woff2’),
url(
https://assets.digitalocean.com/articles/406/roboto-mono-v12-latin-700italic.woff) format(‘woff’),
url(
https://assets.digitalocean.com/articles/406/roboto-mono-v12-latin-700italic.ttf) format(‘truetype’);
font-weight: 700;
font-style: italic;
}
.body {
border-radius: 6px;
background-color: aliceblue;
border: 2px dashed gray;
max-width: 70%;
padding: 1em;
margin-bottom: .4em;
}
.header {
font-family: ‘Roboto Mono’, monospace;
font-weight: 700;
font-style: italic;
}
.paragraph {
font-family: ‘Roboto Mono’, monospace;
}