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.
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.