A friendly tutorial about web fonts and basic typographic principles
“Web typography” refers to the appearance of all the text on
your website. It includes basic CSS text properties
like what font to use and whether it should be italic or not, but typography
is much more than that. It’s about the space between and around letters,
words, and lines. It’s the size of different runs of text in relation to
one another, and the history behind each font family.
A lot of your typography decisions will come from a designer. The only
problem is that typography is an invisible art. To actually understand what
your designer is asking for, you need to be able to see typography the
same way they do.
This chapter isn’t just about the mechanics of adding web fonts to
your site or the CSS properties to move your text around. We’ll also
explain how to properly leverage all these tools to make beautiful,
professional websites. By the end of the chapter, you should not only
know what your designer is talking about when they say something like,
“Can we increase the leading of that paragraph?”, but also
understand why they want you to increase it.
You may forget the specific CSS properties, but the typographic concepts
we’re going to cover will stay with you for the rest of your life because
they aren’t arbitrary rules—they’re grounded in function.
They make your content more readable and help you communicate your message more
effectively.
A Brief History of Web Fonts
We’re going to start this chapter by learning how to display your web
pages in a custom font because that’s the most exciting aspect of modern
web typography. However, web fonts have changed a lot over the last few years,
so before we can start building out our example, we need a little primer on the
various font formats floating around the Internet.
Web Safe Fonts
Long, long ago, web developers only had “web safe fonts” at
their disposal. These were a collection of a dozen or so fonts that were
pre-installed on most computers. There was no such thing as a custom font file
that you could send to browsers to use on your website.
If you needed a special font, your only option was to export an
image of the text you wanted to display and include it in your web page with an
<img/> element. This was ridiculously limiting for web
designers and resulted in some pretty hacky situations for developers.
Honestly, we don’t know how everybody survived through that era of HTML
and CSS.
Custom Web Fonts
Around 2010, browsers began supporting custom web fonts, which was great,
except for the fact that each browser and device required a different file
format. Accordingly, most websites provided 4 different web font files:
File Format
Browser/Device
.svg
Very old Safari (iOS and Desktop)
.eot
Internet Explorer
.ttf
Everything except Internet Explorer
.woff
Newer browsers
This resulted in the “Bulletproof
@font-face syntax”, which you’ll likely
encounter at some point in your web development career.
WOFF Fonts
Recently, the industry has standardized on the Web Open Font Format (WOFF),
so things have gotten a little bit simpler for us. Over 90% of modern browsers support
.woff fonts, and support for its next evolution,
.woff2, is growing. WOFF2 is similar to the original WOFF format,
but offers a significant reduction in file size (which means better
performance).
Eventually, you’ll only need to support WOFF2, but right now, we
suggest providing both WOFF and WOFF2 web fonts to get decent coverage for
older browsers and improved performance on modern ones. Unless legacy browsers
make up a large chunk of your target audience, .ttf,
.svg, and .eot fonts are a thing of the past.
Where to Find Web Fonts
There’s a ton of places on the web where you can download both free
and premium web fonts, but our three favorites are listed below. Again, which
font to use is usually up to your designer (and their budget), but as a
developer, it’s still good to know the trade-offs between these
options.
Note that Font Squirrel and Fontspring offer both web fonts and desktop
fonts (.otf and .ttf files). WOFF is designed
specifically for the needs of the modern web, while desktop fonts contain extra
functionality useful for graphics editing programs like Adobe Illustrator. Be
sure to download or purchase the web font version of the fonts you want to
use—not just the desktop version.
Setup
Ok! We’re ready to experiment with web fonts. We’re going to be building
this example website. We figured you probably don’t want to
start this one from scratch, so go ahead and download the initial project. Unzip it and open
up the web-typography folder with your favorite text editor. If
you don’t have a favorite text editor, you might want to check out Atom.
We’ve got 6 HTML documents all using the same typo.css
stylesheet. We’ll be demonstrating various typographic principles by adding
some page-specific styles to
each of these HTML files.
Open up one of the HTML files with a web browser, and you’ll find that our
initial project is pretty close to the final example, minus all the web fonts
and other CSS typography properties.
Locally Hosted Web Fonts
There are two distinct methods of adding web fonts to your website:
locally hosted or externally hosted. We’ll take a look at both in this
chapter. First, we’ll be adding a locally hosted web font to our example
project. This is a three-step process:
Download a web font and add it to your project.
Embed the web font in your stylesheet.
Use the font elsewhere in your stylesheet.
We’ll be experimenting in the web-fonts.html and
typo.css files. Go ahead and open those up in your text editor
if you haven’t already.
Hosting a WOFF File
So, we need to get our hands on a web font. Our example uses the free Roboto
font, which you should download from Font
Squirrel. Make sure to click the Webfont Kit tab, not the
Download TTF button. Unclick all the formats except
WOFF, since that’s the only one we’ll be using, then click the
Download @font-face Kit button.
This will give you a ZIP file with a license, some instructions, and a
web fonts folder containing a ton of subdirectories. The
Roboto font comes in a bunch of different font faces like light, regular, bold,
italic, and condensed. Each of those folders contains a different face. The one
we want is called roboto_light_macroman. Open up that folder and
copy the Roboto-Light-webfont.woff file into our
web-typography project.
Embedding a Web Font
Sweet. We’ve got a WOFF file. To actually use it in our web page, we
need to embed it into our stylesheet with the @font-face
“at-rule”. Web fonts must always be included at the top of
a stylesheet, so add the following to the very beginning of
typo.css:
The font-family property defines how we’ll refer to this
font later on. This operates as an internal label, so it can be anything you
want. It doesn’t need to relate to the official name of the
font, but it’s usually more intuitive if it does. As we’ll see in a
moment, it’s a good idea to keep the name as generic as possible (e.g.,
Roboto instead of Roboto Light).
Next, we have the src property, which defines the path to the
.woff file via the url() notation. The path can be absolute,
relative, or root-relative. If you use a relative path like we did here, it
will always be relative to the .css file—not the HTML
document. The format() notation lets browsers know which web font
file format it is.
If you reload web-fonts.html page, you won’t see any
change because @font-face only gave us access to our
.woff file. We still need to use it somewhere else in our
stylesheet.
Using a Web Font
Remember from Defining
Fonts that the CSS font-family property defines which font a
particular HTML element uses. After adding our @font-face at-rule,
we can use Roboto as a valid value for font-family
anywhere else in our stylesheet.
Let’s make Roboto Light the default font for our entire example project
by changing the font-family in the body selector of
typo.css:
body {
font-family: 'Roboto', sans-serif; /* Add 'Roboto' here */font-size: 18px;
line-height: 1.8em;
color: #5D6063;
}
Everything should now render as Roboto Light, which means we lost
our comparison with the sans-serif system font in web-fonts.html.
Fix this by adding a page-specific style to
the <head> of our web-fonts.html file:
The .system-fonts class is applied to the second box in
web-fonts.html. The above rule takes precedence over the
body rule in typo.css, so when you open up
web-fonts.html in a browser, you should see our Roboto Light web
font on the top and the default system font on the bottom:
Font Families and Font Faces
A single font “family” is made up of multiple font
“faces”. Each font face is a different weight or style in the
family. “Weight” refers to the boldness of a particular face, and
“style” refers to whether it’s roman (upright), italic,
condensed, extended, or some other variant in the family.
In our example, Roboto Light is one font face in the Roboto family. The
other 17 faces in the ZIP file we downloaded earlier can be visualized like
so:
In CSS, font weights are expressed as numeric values between 100 and 900.
Fortunately, there are relatively standardized, human-friendly terms for each
of these numeric values. “Black” usually means 900,
“bold” is 700, “regular” is 400, etc. As you can see
above, most families don’t supply a face for every single weight. Roboto
is missing “extra light” (200), “semi bold” (600), and
“extra bold” (800).
It’s worth noting that each style and weight combination is designed
as an entirely distinct face. In a high-quality font family, the condensed
styles aren’t simply squashed versions of the roman faces, nor is the
bold face merely a thicker version. Each letter in every face is hand-crafted
to ensure it provides a uniform flow to its text.
This is particularly apparent in the italic and roman faces of many serif
fonts. For instance, the lowercase “a” in Century Schoolbook FS
(the font you’re reading right now) takes on a completely different shape
when it’s italicized.
Fakin’ It
Why does this weight and style stuff matter to us? The design of most
websites utilizes multiple faces in the same family, so we need to know how to
embed several .woff files that represent related faces.
But first, let’s take a look at what happens when we
don’t offer multiple faces. Update the left-hand paragraph in
web-fonts.html to include an <em> and a
<strong> element:
<sectionclass='section section--gray'><h2>Web Fonts</h2><p>This paragraph is using a web font call <em>Roboto Light</em>. It’s a
little more refined and lends some <strong>unique character</strong> to
the web page.</p></section>
When you reload the page, you’ll notice that the bold text isn’t
really all that bold. This is because it’s being synthesized. We
didn’t supply a bold font face for the <strong>
element to use, so the browser is trying to fake it by auto-converting Roboto
Light into a thicker face. The same thing is going on with the italics in the
<em> element, but it’s a little bit harder to tell.
This auto-conversion almost always results in low-quality typography.
To verify that the bold and italic faces really are being synthesized, try
adding the following rule to typo.css. The
font-synthesis property determines if a browser is allowed to fake
it or not. At the time of this writing, only Firefox actually pays attention to
font-synthesis, so this won’t work in Chrome or Safari:
/* This will only work in Firefox */em, strong {
font-synthesis: none;
}
Open up web-fonts.html in Firefox, and the
<em> and <strong> elements will no longer
be italic or bold—the entire paragraph will be in roman Roboto Light.
Multiple Font Faces (The Wrong Way)
Let’s try adding Roboto Light Italic and Roboto Bold faces to our
example project. Copy over the following files from the Roboto ZIP file we
downloaded earlier into our web-typography folder:
A .woff file represents a single face in a particular font
family, and @font-face lets us embed that face in our stylesheet.
The naive way to embed these new WOFF files would be to simply add more
@font-face declarations and change the font-family
and src properties as necessary. Try adding the following to the
top of typo.css:
/* DON'T NAME FONT FAMILIES LIKE THIS */
@font-face {
font-family: 'Roboto Light Italic';
src: url('Roboto-LightItalic-webfont.woff') format('woff');
}
@font-face {
font-family: 'Roboto Bold';
src: url('Roboto-Bold-webfont.woff') format('woff');
}
Then, to use these faces in our <em> and
<strong> elements, we need the following rules:
/* THIS IS A LITTLE AWKWARD */em {
font-family: 'Roboto Light Italic', serif;
}
strong {
font-family: 'Roboto Bold', serif;
}
This will work, and you should now see proper italic and bold fonts
when you reload web-fonts.html in your browser. The problem is
that manually specifying the font-family every time we want to use
an italic or bold font is a little weird. We should be using the CSS
font-style and font-weight properties for this.
We ended up in this awkward situation because of the way we embedded our new
.woff files. Using separate font-family values in
@font-face makes them look like entirely unrelated font faces. It
doesn’t reflect the fact that they are all actually part of the Roboto
family.
For this reason, you should never use the above technique to
embed multiple faces that are in the same font family. Go ahead and
delete both of the above snippets before moving on.
Multiple Font Faces (The Right Way)
To maintain the familial relationship between our three font faces, they all
need to use a shared Roboto value for their
font-family property. To distinguish between our light, italic,
and bold faces, we’ll add font-style and
font-weight properties to the at-rule. Replace all the
@font-face declarations in typo.css with the
following:
Think of each @font-face at-rule as a description of the
underlying .woff file. The first @font-face is saying
it’s a Roboto font that’s roman (normal) and has a
font weight of 300 (aka “light”). The second says it’s also
in the Roboto family and has a weight of 300, but it’s italic. Finally,
the third at-rule lets our the browser know that
Roboto-Bold-webfont.woff contains the 700-weight (aka
“bold”) roman face.
Letting the browser know that our font faces are related makes our CSS much
more intuitive. We can set the default font family and weight in our
body selector. Then, when we want to use italics or bold for a
particular element, we can simply specify a font-style or
font-weight and the browser will pull the corresponding
.woff file:
body {
font-family: 'Roboto', sans-serif;
font-weight: 300;
/* ... */
}
em {
font-style: italic;
}
strong {
font-weight: bold; /* Or 700 */
}
These happen to be the default font-style and
font-weight values for <em> and
<strong> elements, so we don’t really need to
include the last two rules here. Note that the only human-friendly keywords
available for font-weight are normal (400) and
bold (700). Any other boldness levels need to set numerically.
Externally Hosted Web Fonts
Ok! That was complicated. Next, we’re going to explore the easier
method of using web fonts: externally hosted via Google Fonts. This lets us skip the first
two steps of locally hosted fonts. Instead of adding .woff files
to our project and embedding them with @font-face, we can let
Google Fonts do this part for us.
In this section, we’re going to be working on
history.html, so open up that file in both your text editor and a
web browser. If you want a brief history of typography going all the way back
to the first printing press, take a quick read through the example text. Right
now, each section in history.html is using Roboto Light, but
we’re going to change all of them to be representative of the period
they’re talking about.
Let’s begin by changing the font for the Gothic/Blackletter
section. In Google Fonts, search for
UnifrakturMaguntia. It should look like something a monk
wrote in the middle ages. Click Select this font. In the
pop-up menu, you’ll see a <link/> element. Copy this
into the <head> of history.html, above the
<link/> element that includes our typo.css
stylesheet.
Remember that <link/> is how we include an external
stylesheet, and that’s exactly what the above HTML is doing.
However, instead of linking to a local CSS file, it’s including some
CSS defined by Google Fonts. If you paste the href value into your
browser, you’ll find the same @font-face declaration that we
used in the previous section—except we didn’t actually have to
write it this time. Yay!
Now that we’ve embedded our UnifrakturMaguntia web font,
we should be able to use it to style any HTML element we want. Add the
following to the <head> of history.html:
That first section has a class='blackletter' attribute, so it
should now be printed in gothic letters:
Google Fonts are a quick and easy solution, but professional sites should
typically use locally hosted web fonts. This gives you a lot more flexibility
(you’re not limited to Google’s font offering) and can have
performance/reliability gains if you’ve optimized the rest of your site
correctly.
Too Many Font Files
Speaking of performance, let’s do something awful. There’s
another 10 sections on our history.html page, and we want to give
each one its own web font. We can embed multiple fonts in a single
<link/> element, so change our Google Fonts stylesheet to
include the rest of them:
Note that you can generate this in Google Fonts by selecting multiple fonts
before copying the <link/> element. Next, add all these new
fonts to the <style> element of
history.html:
Now, each section of history.html is rendered in a font from
the era it’s describing. This serves as a nice introduction to the
historic significant of different fonts, but you should never, ever
include this many web fonts on a real web page.
Don’t forget that each web font is actually a .woff or
.woff2 file that your browser needs to load before it can render
the page. More fonts means longer load times. The key to using web fonts
effectively is to find a balance between performance (fewer web fonts) and a beautifully typeset
document (more web fonts).
And that’s more than you could ever want to know about web fonts. The
rest of this chapter shifts gears into basic typographic principles. These are
simple guidelines (with simple CSS implementations) that often make the
difference between a professional web page and an amateur one.
Paragraph Indents
Separating paragraphs from one another is one of the most fundamental
functions of typography. There’s two generally accepted solutions: either use
a first-line indent or a margin between the paragraphs. Your readers
(hopefully) aren’t stupid—they don’t need two signs that a
new paragraph is happening, so never use both an indent and a margin.
That would be redundant.
The CSS text-indent property defines the size of the first-line
indent of a particular element (usually a <p>). We can
explore this in our indents.html page. Go ahead and change the
existing bottom margin styles in the first section to an indent by adding the
following rules to the <style> element:
Note that the first paragraph after a heading should never be indented
because, well, it’s usually pretty obvious that it’s a new paragraph. This is a
pretty good use case for the :first-of-type
pseudo-class.
And here’s a negative example so we remember what not to do.
Add this to the page-specific styles in indents.html:
/* DESIGNERS WILL JUDGE YOU FOR THIS */.never-bothp {
text-indent: 1em;
margin-bottom: 1em;
}
It might seem silly, but we’re not kidding when we say that good
designers will judge you for this.
Text Alignment
The alignment of text has a subconscious impact on how you read it.
You’ve probably never noticed it before, but your eyes don’t move
in a smooth motion as they read over a paragraph—they jump from word to
word and from line to line. Your eyes fixate on certain spots and skip over
other ones.
In a well-designed HTML document, text alignment is never an arbitrary
decision. It takes into account this little bit of human physiology. Good text
alignment actually makes it easier for users to read your content by giving
their eyes an anchor to jump to when they move from line to line.
The next few sections explain the proper times to use left, center, right,
and justified text alignment. All of these examples rely on the text-align
property, which controls the text alignment of a particular HTML element.
We set up the alignment.html page in our example project with some
convenient scenarios.
Left Alignment
Most of your text should be left-aligned because it gives the reader a
vertical anchor to jump back to on every line. Long runs of text, in
particular, should almost always be left-aligned. Short runs of text and
headings have a little bit more leeway.
Left alignment is the default value for text-align, but if we
wanted to be explicit, we could add the following rule to the
<style> element of our alignment.html file:
<style>.left {
text-align: left;
}
</style>
Of course, if you’re working on a website that’s in a language
that’s written right-to-left instead of left-to-right (like Arabic), you
can go ahead and swap all this advice with the Right Alignment section
below.
Center Alignment
Center-aligned text doesn’t have that anchor, so it’s easier for
the eye to get lost when it tries to jump to the next line. It’s best
suited for short line lengths (more on that later) and for special kinds of
content like poems, lyrics, and headings.
Go ahead and center-align the second paragraph in
alignment.html with another page-specific style:
.center {
text-align: center;
}
Notice how the page now feels a little disjointed. The center-aligned second
paragraph breaks the flow of the left-aligned first paragraph. Generally
speaking, text alignment should be consistent throughout a web page. If
you’re going to center a heading, center all of your
headings.
Right Alignment
Another consideration when choosing text alignment is the relationship it
creates with the surrounding elements. For instance, take a look at that third
section in alignment.html. We want to move the image’s
caption to the left of the image and right-align it to make it look like
it’s attached to the image:
Our example image is wrapped in a <figure> and the caption text
is in a <figcaption>, so adding the
following to the <style> element of
alignment.html should result in the above layout.
This also happens to be a good example of advanced positioning. The
relative position of the <figure> sets the coordinate
system for the <figcaption>’s absolute
positioning. By nudging the caption left by 220px and giving it an
explicit width of 200px, we get a nice 20-pixel margin between the
image and its caption.
Like centered text, right alignment should usually be reserved for these
kinds of special design scenarios because its jagged left edge makes it
harder for the reader to find the next line.
Justified Text
Justified text is created by subtly adjusting the space between
words/letters and splitting long words with hyphens until each line is the same
width. Without a high-quality hyphenation engine, justified text results in
awkwardly large spaces between words. These uneven spaces make it harder for
the eye to move horizontally across the text.
Unfortunately, most browsers don’t have any kind of built-in
hyphenation engine, so you’re better off avoiding justified text in HTML
documents. We can take a look by adding one more text-align rule
to our alignment.html file:
.justify {
text-align: justify;
}
Compare this with the left-aligned paragraph. It’s subtle, but the
left-aligned paragraph is more uniform and inviting.
Vertical Text Spacing
Just as alignment isn’t an arbitrary decision, neither is the space
between text. In this section, we’re concerned with the responsible
use of three CSS properties:
margin-top (or padding-top)
margin-bottom (or padding-bottom)
line-height
The first two should be pretty familiar by now, and they define the vertical
space between separate paragraphs. The new line-height property
determines the amount of space between lines in the same paragraph. In
traditional typography, line-height is called
“leading” because printers used little strips of lead
to increase the space between lines of text.
Together, these properties control the “vertical rhythm” of a
web page. There’s all sorts of techniques to figure out the
“optimal” vertical rhythm for a given layout, but the general principles
are:
Give things enough space to breath.
Use consistent spacing throughout the page.
To demonstrate this, we’re going to destroy the vertical rhythm in the
second half of our spacing.html page. Go ahead and add the
following page-specific styles to spacing.html
A few small changes to line height, paddings, and margins can have a
dramatic impact on the quality of a page:
There’s a surprising amount of math and psychology that goes into
calculating the vertical rhythm of a page, but that’s a job for your
designer. As a developer, you need to know the CSS properties to implement what
they’re asking for. More importantly, you have to understand that your
designer really cares about this kind of stuff, so you should be paying very
careful attention to your margin, padding, and
line-height properties.
Line Length
If the vertical spacing of your text isn’t arbitrary, it should be no
surprise that the horizontal spacing isn’t, either. “Line
length” or “measure” refers to the horizontal length of your
text. You can think of it as the number of characters or words that fit into a
single line. Measure has everything to do with the following CSS
properties:
width
margin-left (or padding-left)
margin-right (or padding-right)
A good rule-of-thumb is to limit the number of characters on a single line
to around 80. Like alignment, this subtly affects the readability of your
content. It takes energy for your eye to move from the left edge of a paragraph
to the right, and the farther it has to scan, the faster it gets tired. Longer
lines also make it easier to get lost when you finish a line and need to jump
back to the beginning of the next line.
These are the reasons why so many websites (including this one) use fixed-width layouts or split
content into multiple columns on wider screens. Without constraining the width
of the page or dividing it into manageable columns, line length becomes
unacceptably long.
In our example project, the line-length.html file has decent
measure. Let’s see what happens when we break the bottom half of the page
by adding the following to its <head>:
<style>
@media only screen and (min-width: 580px) {
.not-so-manageable {
max-width: 100%;
margin-left: 2em;
margin-right: 2em;
}
}
</style>
Now, the second section stretches to fill the full width of the browser
window. It feels a little bit more unapproachable due to the long line length.
Again, the goal of good web typography is to make it as easy as possible for
visitors to digest your content.
Other Basic Typography Guidelines
That should be enough to get you on your way towards quality web typography.
Typography is a whole industry, and we’ve barely scratched the surface.
However, getting any deeper into it would be more design than web development,
so we’ll just leave you with a few final guidelines:
Use a font-size between 14px and
20px for the body element.
Use “curly quotes” and apostrophes with the
’, ‘,
”, and “ HTML entities.
Use proper dashes (–, —)
and other symbols (&copy;).
Don’t use text-decoration: underline except for hover
states.
Use real italic fonts over synthesized ones if not it’s too
much of a performance burden.
If you find this stuff fascinating, Practical
Typography has a fantastic list of general rules to follow when
typesetting a document.
Summary
The goal of this chapter was twofold:
Learn the mechanics of web fonts and basic CSS typography
properties.
Understand how designers think about typography.
You might not be able to create a beautifully typeset web page from scratch
after reading this chapter, but that wasn’t the point. It was to make you
aware of the invisible art of typography. You should now have the
vocabulary to talk about things like font families, faces, weights, and styles,
as well as leading, measure, and vertical rhythm.
The most important thing you should take away from this chapter is the fact
that nothing is arbitrary in a well-designed web page. The font sizes, indent
style, text alignment, line height, margins, and every other tiny facet of the
page was carefully considered. There was a purpose behind all of these
decisions.
All of the CSS properties we’ve covered throughout this tutorial are
actually kind of simple. When it comes down to it, we’ve really just been
moving a bunch of boxes around, changing some colors, and altering the
appearance of our text. The meaning behind these things comes from
the underlying design and the business goals of the website you’re
implementing.
But, that’s for another tutorial. Believe it or not, you’ve reached the end of HTML & CSS is Hard. We’ve covered all
the HTML elements and CSS properties you need to build professional web pages.
The only thing missing is experience. Your next step is to practice all
these new skills by building a bunch of web pages from scratch.