Lesson 37: Text and Typography

Text is one of the core building blocks of the web.

When you make a website, you don’t have to style your text. HTML has some pretty reasonable default styling on its own.

Even so, text will likely make up most of your website. That makes it worth adding some styling to spruce it up. By changing a few basic properties, you can make the reading experience much better for your users.

In this lesson, we’ll start with the @font-face rule, which lets you bring custom fonts into your webpages. This makes sure you get the exact typography you need, no matter which fonts a user has installed.

After that, we’ll cover the main CSS font properties, including font-family, font-style, font-weight, and font-size. These basics set the stage for shaping text for both style and readability.

We’ll also touch on paragraph properties like text-indent and word-spacing. We’ll finish with more advanced topics such as variable fonts and pseudo-elements, which give you even more control over your type.

There are practical examples and tips throughout to help the ideas stick.

The @font-face rule

The @font-face CSS at-rule is a key tool in web design. It lets you pick and use custom fonts to display text. The nice thing about @font-face is how flexible it is. It lets you load fonts from a remote server, or from a font installed on the user’s device.

Syntax

@font-face {
  font-family: "Trickster";
  src:
    local("Trickster"),
    url("trickster-COLRv1.otf") format("opentype") tech(color-COLRv1),
    url("trickster-outline.otf") format("opentype"),
    url("trickster-outline.woff") format("woff")
}

Descriptors

ascent-override
Customizes the ascent metric, which is the space above the baseline.
descent-override
Adjusts the descent metric, which is the space below the baseline.
font-display
Controls how the font shows while it is still downloading.
font-family
Names the font so you can use it in font-related properties.
font-stretch
Sets the allowed horizontal scaling, as a single value or a range.
font-style
Defines the font style, with angle ranges for oblique styles.
font-weight
Sets the font’s weight, or the range of weights available.
font-feature-settings
Turns on access to OpenType font features.
font-variation-settings
Gives you fine-tuned control over variable font settings.
line-gap-override
Overrides the font’s default line gap.
size-adjust
Applies a scaling factor to the font’s outline and metrics.
src
Defines the font source, whether local or remote. This is required for the @font-face rule. Combining url() and local() in the src is a common trick. It uses a local font if one is available, and falls back to a remote font file if not. Browsers prioritize resources by the order you list them, so local() should usually come before url().
unicode-range
Limits which characters this font is used for.

Description

@font-face frees designers from the limits of “web-safe” fonts by letting them use custom typography. The local() function can search for a font on the user’s device. That offers a smooth experience that doesn’t always need an internet connection.

[!NOTE] For compatibility tips and more details on older browsers, see the src descriptor docs. You can also nest @font-face inside CSS conditional group rules.

Font MIME types

FormatMIME Type
TrueTypefont/ttf
OpenTypefont/otf
Web Open Font Formatfont/woff
Web Open Font Format 2font/woff2

[!NOTE] Web fonts follow the same-origin policy. You can set them up for cross-origin use with HTTP access controls.

[!NOTE] You can’t declare @font-face inside a CSS selector block.

The difference between @font-face and font-family

In CSS, @font-face and font-family are often mixed up, but they do different jobs.

As we’ve discussed, @font-face is a rule that defines any custom fonts you want to use in your web application. It tells the browser where to download the font. It tells the browser how to display the font while it loads, using the font-display property. It also tells the browser which subset of characters to download, using the unicode-range.

In contrast, font-family is a CSS property used inside a CSS rule. It assigns a font, or a list of fonts, to an element. The fonts listed under font-family can be web-safe fonts, system fonts, or custom fonts defined with @font-face.

To sum up, @font-face declares a font and gives it a name. The font-family property then applies that declared font to HTML elements.

Here’s an example of using both.

@font-face {
  font-family: "CustomFont";
  src: url("customfont.woff2") format("woff2");
}

body {
  font-family: "CustomFont", Arial, sans-serif;
}

In this example, @font-face defines “CustomFont” and tells the browser where to find it. The font-family property then applies it to the body element, with Arial as a fallback if “CustomFont” isn’t available.

Change the typeface

Use font-family to change the typeface of your text.

The font-family property takes a comma-separated list of strings. Each one refers to a specific or generic font family. Specific font families are quoted strings, such as “Helvetica”, “EB Garamond”, or “Times New Roman”. Generic font families are keywords such as serif, sans-serif, and monospace. You can find the full list of options on MDN. The browser displays the first available typeface from the list you give it.

[!NOTE] When the browser picks which font to display from your font-family list, it doesn’t stop at the first font it can use. Instead, it chooses fonts one character at a time. If a character isn’t in the first font in the list, the browser moves on to the next font, until it reaches the end of the list.

When you use font-family, you should name at least one generic font family. That way you’re covered if the user’s browser doesn’t have your preferred fonts. The fallback generic font family should usually be similar to your preferred fonts. For example, if you use font-family: "Helvetica", which is a sans-serif font, your fallback should be sans-serif to match.

Use italic and oblique fonts

Use font-style to set whether text should be italic or not. font-style takes one of these keywords - normal, italic, and oblique.

[!NOTE] What’s the difference between italic and oblique? In fonts that support it, font-style: italic is usually a cursive version of the regular typeface. font-style: oblique shows a slanted version of the regular typeface.

Make text bold

Use font-weight to set the boldness of text. This property takes keyword values like normal and bold, relative keyword values like lighter and bolder, and numeric values from 100 to 900.

The keywords normal and bold are the same as the numeric values 400 and 700.

The keywords lighter and bolder are worked out relative to the parent element. See MDN’s Meaning of Relative Weights for a handy chart that shows how this value is found.

[!NOTE] Most fonts, especially the “web-safe” ones, only support the weights 400 (normal) and 700 (bold). When you import fonts with @font-face or @import, you can pick the specific weights you want to pull in. Still, non-variable fonts only support numeric values for font-weight in the 100s, like 100, 200, 300, and so on. If you want to use font-weight: 321, for example, you’ll have to use a Variable Font.

Change the size of text

Use font-size to control the size of your text elements. This property takes length values, percentages, and a handful of keyword values.

On top of length and percentage values, font-size takes some absolute keyword values. These are xx-small, x-small, small, medium, large, x-large, and xx-large. It also takes a couple of relative keyword values, smaller and larger. The relative values are relative to the parent element’s font-size.

[!NOTE] What’s the difference between em and rem? In CSS, em is the font-size inherited from the element’s parent. For example, font-size: 2em is the same as the parent’s font-size times two. rem is similar, but it is the font-size inherited from the root element, such as <html>.

Change the space between lines

Use line-height to set the height of each line in an element. This property takes a number, a length, a percentage, or the keyword normal. It’s usually best to use a number instead of a length or percentage to avoid issues with inheritance.

Change the space between characters

Use letter-spacing to control the horizontal space between characters in your text. This property takes length values such as em, px, and rem.

Note that the value you set increases the natural space between characters. In the demo below, try selecting a single letter to see the size of its letterbox and how it changes with letter-spacing.

Change the space between words

Use word-spacing to grow or shrink the space between each word in your text. This property takes length values such as em, px, and rem. Note that the length you set is for extra space on top of the normal spacing. This means word-spacing: 0 is the same as word-spacing: normal.

font shorthand

You can use the shorthand font property to set many font-related properties at once.

The properties you can set are font-family, font-size, font-stretch, font-style, font-variant, font-weight, and line-height.

Check out MDN’s font article for how to order these properties.

Change the case of text

Use text-transform to change the capitalization of your text without changing the underlying HTML. This property takes the keyword values uppercase, lowercase, and capitalize.

Add underlines, overlines, and through-lines to text

Use text-decoration to add lines to your text. Underlines are the most common, but you can also add lines above your text or right through it.

The text-decoration property is shorthand for the more specific properties below.

The text-decoration-line property takes the keywords underline, overline, and line-through. You can also list more than one keyword for more than one line.

The text-decoration-color property sets the color of all the lines from text-decoration-line.

The text-decoration-style property takes the keywords solid, double, dotted, dashed, and wavy.

The text-decoration-thickness property takes any length value. It sets the stroke width of all the lines from text-decoration-line.

The text-decoration property is a shorthand for all the properties above.

[!NOTE] Use text-underline-position to offset the underline of a text-decoration: underline by the amount you set. This property doesn’t work for overline or line-through.

Add an indent to your text

Use text-indent to add an indent to your blocks of text. This property takes either a length, like 10px or 2em, or a percentage of the containing block’s width.

Deal with overflowing or hidden content

Use text-overflow to set how hidden content is shown. There are two options. The clip value, which is the default, cuts the text off at the point where it overflows. The ellipsis value shows an ellipsis (…) at the point where it overflows.

Control white-space

The white-space property sets how whitespace in an element is handled. For more details, check out the white-space article on MDN.

white-space: pre can be handy for rendering ASCII art or carefully indented code blocks.

Control how words break

Use word-break to change how words break when they would overflow the line. By default, the browser won’t split words. Using the keyword value break-all for word-break tells the browser to break words at individual characters if it needs to.

Change text alignment

Use text-align to set the horizontal alignment of text in a block or table-cell element. This property takes the keyword values left, right, start, end, center, justify, and match-parent.

The values left and right align the text to the left and right sides of the block.

Use start and end for the start and end of a line of text in the current writing mode. So start maps to left in English, and to right in Arabic script, which is written right to left (RTL). These are logical alignments. Learn more in our logical properties module.

Use center to align the text to the center of the block.

The value justify lays out the text and changes word spacing on its own. That way the text lines up with both the left and right edges of the block.

Control how text wraps

Use text-wrap to change how text inside an element wraps.

The keywords for this property are wrap, nowrap, balance, and stable. The default value is wrap, which keeps overflow to a minimum by wrapping text across lines at normal spaces and word breaks.

You can use the nowrap keyword to do the opposite and stop the text from breaking across lines, which can cause overflow.

To get the same amount of text on each line, for example in headings or headlines, use the balance keyword. To keep things fast, browsers only apply this value to elements with six lines of text or fewer.

The stable keyword works much like wrap, but it is meant for contenteditable text. With text-wrap: stable set, the lines above the content you are editing won’t shift around while you type.

Sometimes long strings with no clear breaking point can overflow their containers. To control how this kind of text breaks, use overflow-wrap.

The keywords for this property are normal, break-word, and anywhere. The default setting is normal, which won’t break text onto the next line unless it has spaces or natural break points.

The anywhere and break-word values add break points anywhere inside the string to prevent overflow. The keywords differ in how they react to an intrinsic or explicit min-content size. The anywhere keyword allows all of the soft breaking opportunities it can. The break-word value doesn’t, and would make the text as long as the longest word.

Change the direction of text

Use direction to set the direction of your text, either ltr (left to right, the default) or rtl (right to left). Some languages like Arabic, Hebrew, or Persian are written right to left, so you should use direction: rtl. For English and all other left-to-right languages, use direction: ltr.

[!CAUTION] In general, you should favor the HTML attribute dir over direction. Check out this StackOverflow discussion for more details.

Change the flow of text

Use writing-mode to change the way text flows and is arranged. The default is horizontal-tb. You can also set writing-mode to vertical-lr or vertical-rl for text that you want to flow vertically.

Change the orientation of text

Use text-orientation to set the orientation of characters in your text. The valid values for this property are mixed and upright. This property only matters when writing-mode is set to something other than horizontal-tb.

Add a shadow to text

Use text-shadow to add a shadow to your text. This property expects three lengths (x-offset, y-offset, and blur-radius) and a color.

Check out the text-shadow section of our module on Shadows to learn more.

Variable fonts

Usually, “normal” fonts need you to import different files for different versions of the typeface, like bold, italic, or condensed. Variable fonts are fonts that can hold many different variants of a typeface in one file. For example, Roboto Flex offers random combinations of width and weight.

Check out our article on Variable Fonts for more details.

Pseudo-elements

[!IMPORTANT] A pseudo-element is a part of an element that you can target with CSS keywords without adding more HTML. Check out our module on pseudo-elements for a deep dive into this subject.

::first-letter and ::first-line pseudo-elements

The ::first-letter and ::first-line pseudo-elements target a text element’s first letter and first line.

::selection pseudo-element

Use the ::selection pseudo-element to change how user-selected text looks.

When you use this pseudo-element, only certain CSS properties work. These are color, background-color, text-decoration, text-shadow, stroke-color, fill-color, and stroke-width.

font-variant

The font-variant property is a shorthand for a number of CSS properties. These let you pick font variants like small-caps and slashed-zero.

The CSS properties this shorthand includes are font-variant-alternates, font-variant-caps, font-variant-east-asian, font-variant-ligatures, and font-variant-numeric. Check out the links on each property for more details about how to use it.

Resources

Here are some resources to learn more about styling text on the web.


Adapted from Learn CSS © Google and contributors, licensed under CC BY 4.0 (prose) and Apache 2.0 (code samples).