Course topics

By WebNest Studio Engineering Team

CSS Tutorial

CSS Typography Fundamentals

Typography controls how text looks and reads on a page, and it is one of the highest-leverage areas of CSS — good typography improves readability and perceived quality even when nothing else on the page changes. The core typography properties are font-family, font-size, line-height, and font-weight, and they interact closely with each other.

Beyond individual properties, you also need to decide where your fonts come from: the small set of web-safe fonts that are pre-installed on most devices, or custom fonts loaded with @font-face that give full control over a brand's look but require careful loading strategy.

Why it matters

Most of a web page is text, and the difference between an amateur page and a professional one is largely type: a readable size, comfortable line spacing and lines that are not too long. These few properties cost nothing and improve every page you make.

Core Typography Properties

Four properties form the backbone of any typographic system.

  • font-family — a prioritized list of typefaces, ending in a generic fallback (serif, sans-serif, monospace), e.g. font-family: "Helvetica Neue", Arial, sans-serif;.
  • font-size — the size of the text, ideally set in rem for accessibility (see the Units topic).
  • line-height — the vertical space a line of text occupies; a unitless value like 1.5 is preferred because it scales proportionally with font-size.
  • font-weight — controls boldness, from 100 (thin) to 900 (black), with 400 being normal and 700 being bold in most typefaces.

Web-Safe Fonts vs. @font-face

Web-safe fonts (Arial, Georgia, Times New Roman, Verdana, and a handful of others) are pre-installed on nearly every operating system, so they render instantly with no download and are a safe fallback choice. To use a custom typeface that isn't web-safe, @font-face lets you load a font file directly: @font-face { font-family: "Brand Sans"; src: url("brand-sans.woff2") format("woff2"); }. Custom fonts add a network request and can cause a brief flash of unstyled or fallback text, so they should be used deliberately and typically paired with font-display: swap.

Readable Line-Height and Measure

A line-height between 1.4 and 1.6 is generally considered comfortable for body text, and pairing it with a reasonable line length (measure) — often capped with max-width: 65ch — keeps paragraphs from becoming too wide to track easily with the eye.

Examples

A typical body-text style combining font stack, size, line-height, and measure

CSS
body {
  font-family: "Segoe UI", Roboto, Arial, sans-serif;
  font-size: 1rem;
  line-height: 1.6;
  color: #222;
}

p {
  max-width: 65ch;
}

h1 {
  font-weight: 700;
  line-height: 1.2;
}
Output
Body paragraphs render with comfortable line spacing and a readable line length capped at roughly 65 characters, while headings stay tighter and bolder for visual hierarchy.

Loading a custom font with @font-face and a fallback stack

CSS
@font-face {
  font-family: "Brand Sans";
  src: url("/fonts/brand-sans.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

h1 {
  font-family: "Brand Sans", Arial, sans-serif;
}
Output
Headings display in the custom "Brand Sans" typeface once it loads; until then, font-display: swap shows the Arial fallback immediately instead of invisible text.

Common Mistakes

  • Forgetting a generic fallback (sans-serif, serif, monospace) at the end of a font-family list, risking an unstyled system default if every listed font fails to load.
  • Setting line-height in px, which does not scale properly if font-size changes via media queries or user settings — a unitless value is safer.
  • Loading multiple font weights and styles via @font-face without font-display, causing a noticeable flash of invisible text (FOIT) while the font downloads.
  • Using overly long line lengths on wide screens without a max-width or ch-based cap, making body text tiring to read.

Exercise

Set readable body text: a system font stack with a generic fallback, a line height of 1.6, and paragraphs no wider than 65 characters. Make the heading bold at weight 700 with letter spacing tightened by 0.02em.

Expected result: comfortable, evenly spaced text with a slightly tightened heading.

CSS · starter code
/* The page contains: <main> with an <h1>, a <p> and a <button> */

body {
  /* TODO: font stack and line height */
}

p {
  /* TODO: limit the line length */
}

h1 {
  /* TODO: weight and letter spacing */
}
Show hints
  • A font stack lists fonts in order of preference and ends with a generic family such as sans-serif.
  • The ch unit is about the width of one character, so max-width: 65ch limits the line length.
Show solution
CSS · solution
/* The page contains: <main> with an <h1>, a <p> and a <button> */

body {
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  line-height: 1.6;
}

p {
  max-width: 65ch;
}

h1 {
  font-weight: 700;
  letter-spacing: -0.02em;
}

Quick quiz

1. Why does a font stack end with a generic family such as sans-serif?

  • A. It is required by the syntax
  • B. It guarantees a fallback if none of the named fonts is available
  • C. It makes the text bold
  • D. It loads the font faster
Show answer

B. The browser works through the list and uses the first font it has.

2. Why is a unitless line-height, such as 1.5, recommended?

  • A. Units are not allowed
  • B. It is shorter to type
  • C. It is multiplied by each element's own font size, so it scales correctly when inherited
  • D. It disables inheritance
Show answer

C. A value with a unit is computed once and inherited as a fixed length, which can be too small for larger text.

3. Which rule loads a custom font file?

  • A. @import-font
  • B. @font
  • C. @typeface
  • D. @font-face
Show answer

D. It names a font family and points to the font file with src.

Interview questions

What makes body text readable?

Show answer

A font size of at least 16px for body text; a line height of roughly 1.5 to 1.6; a line length of about 45 to 75 characters, which max-width in ch enforces; strong contrast between text and background; and left-aligned text for long passages. Enough space between paragraphs and a clear difference between heading levels complete it.

What does font-display: swap do?

Show answer

It is a descriptor in an @font-face rule that controls what happens while a web font is downloading. With swap, the browser shows the text straight away in a fallback font and switches to the web font when it arrives. This avoids invisible text during loading, at the cost of a visible change of font; choosing a fallback of similar proportions reduces that shift.

Key Points to Remember

  • font-family, font-size, line-height, and font-weight are the core levers for typographic control.
  • Web-safe fonts render instantly with no download; @font-face enables custom brand fonts at the cost of a network request.
  • A unitless line-height (around 1.4–1.6 for body text) scales cleanly with font-size changes.
  • font-display: swap avoids invisible text while a custom web font is downloading.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Shows a live preview of your HTML, CSS and JavaScript. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering