Lesson 5 of 12

CSS Text and Fonts

Most of the web is text, so typography has a huge effect on how a site feels and how easy it is to use. CSS gives you control over the typeface, size, weight, spacing and alignment of text. In this lesson you will learn the CSS fonts and text properties that matter most for readable, good-looking text.

Choosing CSS fonts with font-family

The font-family property takes a font stack: a comma-separated list of fonts. The browser uses the first one that is available on the device and falls back to the next. Always end with a generic family such as sans-serif, serif or monospace so there is always something to use.

The generic name system-ui uses the operating system’s own interface font (Segoe UI on Windows, San Francisco on Apple devices, Roboto on many Android phones). It loads instantly because nothing is downloaded, which is why many modern sites use it for body text.

Font names with spaces should be in quotes, for example "Noto Sans Tamil". Because font-family is inherited, you usually set it once on body and only change it for headings or code.

Size, weight and style

PropertyExampleWhat it does
font-size1.125remHow large the text is
font-weight400, 700Thickness, from 100 (thin) to 900 (black); bold equals 700
font-styleitalicItalic or normal
line-height1.6Space between lines of text
fontitalic 700 1.25rem/1.4 serifShorthand for several font properties

Use rem for font sizes. 1rem equals the root font size, which is 16px by default but changes if the user has set a larger text size in their browser. Pixel font sizes ignore that preference, which hurts people with low vision. You will learn more about units in CSS Units.

For line-height, use a plain number with no unit, such as 1.5. It is multiplied by each element’s own font size, so headings and small text each get proportional spacing. Body text reads best between about 1.4 and 1.7.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>A readable text setup</title>
<style>
body {
  font-family: system-ui, "Segoe UI", Roboto, sans-serif;
  font-size: 1.0625rem;
  line-height: 1.6;
  color: #1f2328;
  max-width: 65ch;          /* comfortable line length */
  margin-inline: auto;
  padding: 20px;
}

h1 {
  font-family: Georgia, "Times New Roman", serif;
  font-size: 2.25rem;
  font-weight: 700;
  line-height: 1.2;
}

.lead { font-size: 1.25rem; color: #555; }

code { font-family: ui-monospace, Consolas, monospace; background: #f2f2f2; padding: 0 4px; }
</style>
</head>
<body>
<h1>A weekend in Hampi</h1>
<p class="lead">Boulders, temples and sunsets over the Tungabhadra river.</p>
<p>Hampi was the capital of the Vijayanagara Empire. Today you can explore the ruins by bicycle, starting early to avoid the afternoon heat. Carry water and wear comfortable shoes.</p>
<p>This column is limited with <code>max-width: 65ch</code>, roughly 65 characters per line.</p>
</body>
</html>

Tip: Long lines are tiring to read. Limiting text containers to around 60 to 75 characters with max-width: 65ch is one of the easiest ways to make articles feel more comfortable. The ch unit is roughly the width of the “0” character.

Aligning and decorating text

  • text-align: start, center, end or justify. start and end respect right-to-left languages, unlike left and right.
  • text-decoration: add or remove underlines, for example text-decoration: none on navigation links. text-underline-offset moves the underline away from the letters.
  • text-transform: uppercase, lowercase or capitalize without changing the HTML.
  • letter-spacing and word-spacing: fine-tune spacing. Small uppercase labels often look better with a little extra letter spacing.
  • text-wrap: balance: evens out line lengths in headings so you do not get one lonely word on the last line. text-wrap: pretty does a gentler version for paragraphs in browsers that support it.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Alignment, transforms and spacing</title>
<style>
body { font-family: system-ui, sans-serif; padding: 20px; line-height: 1.5; }

.label {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.75rem;
  font-weight: 600;
  color: #0b7285;
}

h2 {
  text-wrap: balance;
  max-width: 22ch;
}

.quote {
  font-style: italic;
  text-align: center;
  font-size: 1.25rem;
}

a {
  color: #0b5394;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}
</style>
</head>
<body>
<p class="label">Travel guide</p>
<h2>Five quiet beaches on the Konkan coast you can reach by train</h2>
<p class="quote">"Slow travel is the best travel."</p>
<p>Read the <a href="#">full itinerary</a> before you book.</p>
</body>
</html>

Loading web fonts

If you need a font that is not installed on users’ devices, you can load it with the @font-face rule. You give the font a name, point to the file and then use that name in font-family. Use the compressed WOFF2 format, which every modern browser supports.

The font-display: swap descriptor tells the browser to show text in a fallback font straight away and swap to the web font once it has downloaded, so visitors are never staring at invisible text. Because the try-it editor cannot load external files, this example uses local() to look for a font already on the device:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@font-face with a fallback</title>
<style>
@font-face {
  font-family: "Brand Sans";
  /* on a real site: src: url("/fonts/brand-sans.woff2") format("woff2"); */
  src: local("Arial");
  font-weight: 400 700;
  font-display: swap;
}

body {
  font-family: "Brand Sans", system-ui, sans-serif;
  padding: 20px;
}

.bold { font-weight: 700; }
</style>
</head>
<body>
<p>This text uses the "Brand Sans" family declared with @font-face.</p>
<p class="bold">If the font is missing, the browser falls back to system-ui.</p>
</body>
</html>

Watch out: Every font file and weight is an extra download. Loading six weights of two families can slow your page noticeably. Pick one or two families, only the weights you use, and consider a variable font, which contains many weights in a single file.

When you choose fonts for scripts such as Tamil, Hindi or Malayalam, check that the font actually contains those characters. Otherwise the browser silently mixes in a fallback font for some letters and the text looks uneven.

Choosing CSS fonts well is also a design skill; see Typography for UI in the UI/UX track. Next you will learn how boxes are laid out with CSS Display and Position.

Further reading

Key takeaways

  • Write font-family as a stack and end with a generic family such as sans-serif.
  • Use rem for font sizes so text respects the user's browser settings.
  • Use unitless line-height (about 1.5) and limit line length with max-width: 65ch.
  • Prefer text-align: start and end; use text-wrap: balance for headings.
  • Load custom fonts with @font-face, WOFF2 files and font-display: swap, and keep the number of files small.

Quick quiz

Check what you learned – pick an answer.

1. Why should a font stack end with a generic family like sans-serif?

2. Which line-height value scales correctly with each element's font size?

3. What does font-display: swap do?

Practice exercise

Style a blog article: use a system font stack for body text at 1.125rem with line height 1.7, a serif stack for the heading with balanced wrapping, limit the article to 65 characters wide and centre it, and make a small uppercase category label with extra letter spacing.

Show the solution
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Article typography</title>
<style>
body { padding: 20px; color: #222; }

article {
  font-family: system-ui, sans-serif;
  font-size: 1.125rem;
  line-height: 1.7;
  max-width: 65ch;
  margin-inline: auto;
}

article h1 {
  font-family: Georgia, serif;
  line-height: 1.2;
  text-wrap: balance;
}

.category {
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: 0.8rem;
  font-weight: 600;
  color: #b5462b;
}
</style>
</head>
<body>
<article>
  <p class="category">Food</p>
  <h1>How to make soft idlis with a simple home-ground batter</h1>
  <p>Soak rice and urad dal separately for six hours, grind them, mix and let the batter ferment overnight in a warm place.</p>
</article>
</body>
</html>

Progress is saved in this browser only.