On this page

Web typography

10 min read TextCh. 4 — Visual Styles

The importance of typography

Typography is responsible for 95% of web design. A good typographic system improves readability, establishes visual hierarchy, and conveys the brand's personality.

System fonts vs web fonts

System font stack

System fonts load instantly because they are already installed on the user's device:

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

Each operating system has its native font: San Francisco on macOS, Segoe UI on Windows, Roboto on Android. The system-ui stack automatically selects the correct one.

Google Fonts

To use custom web fonts, Google Fonts is the most popular option. To optimize loading:

  1. Use <link rel="preconnect"> for early connection
  2. Load only the weights you need (400, 500, 700)
  3. Use font-display: swap to avoid invisible text

@font-face for self-hosted fonts

If you host your own fonts, use @font-face:

@font-face {
  font-family: "MyFont";
  src: url("/fonts/my-font.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

The WOFF2 format is the most efficient and has universal support in modern browsers.

Essential typographic properties

font-size

Defines the text size. Use relative units (rem, em) instead of px to respect user preferences:

html { font-size: 100%; }  /* Respects browser config (16px) */
body { font-size: 1rem; }  /* 16px by default */
h1   { font-size: 2.5rem; } /* 40px */

line-height

Line height is crucial for readability. A value between 1.5 and 1.75 is recommended for body text:

body { line-height: 1.6; }       /* General text */
h1   { line-height: 1.2; }       /* Headings: more compact */
.compact { line-height: 1.4; }   /* UI: buttons, navs */

letter-spacing and word-spacing

Adjust the space between letters and words. Use sparingly:

.large-title {
  letter-spacing: -0.02em;  /* Large titles: tighter */
}
.subtitle {
  letter-spacing: 0.05em;   /* Small uppercase text */
  text-transform: uppercase;
}

font-weight

Controls the thickness of text. Variable fonts allow any value between 100 and 900:

Value Name
100 Thin
300 Light
400 Regular
500 Medium
700 Bold
900 Black

Typographic scale

A consistent typographic scale creates harmonious visual hierarchy. A common ratio is 1.25 (Major Third):

:root {
  --text-xs: 0.75rem;    /* 12px */
  --text-sm: 0.875rem;   /* 14px */
  --text-base: 1rem;     /* 16px */
  --text-lg: 1.125rem;   /* 18px */
  --text-xl: 1.25rem;    /* 20px */
  --text-2xl: 1.5rem;    /* 24px */
  --text-3xl: 2rem;      /* 32px */
  --text-4xl: 2.5rem;    /* 40px */
}

Fluid typography with clamp()

Instead of using media queries to change font sizes, use clamp():

h1 { font-size: clamp(2rem, 5vw, 3.5rem); }

This creates a font size that:

  • Never goes below 2rem (32px)
  • Scales with 5% of the viewport width
  • Never exceeds 3.5rem (56px)

The prose system

For long-form content (articles, documentation), create a .prose class that establishes all typographic rules:

.prose {
  max-width: 65ch;
  font-size: 1.125rem;
  line-height: 1.75;
}

.prose > * + * {
  margin-block-start: 1.25em;
}

.prose h2 { margin-block-start: 2em; }

.prose code {
  font-family: "Fira Code", monospace;
  font-size: 0.875em;
  background: #f0f0f0;
  padding: 0.125em 0.375em;
  border-radius: 4px;
}

text-wrap: balance and pretty

Modern CSS introduces control over line wrapping in headings and paragraphs:

h1, h2, h3 {
  text-wrap: balance; /* Balances the heading lines */
}

p {
  text-wrap: pretty; /* Avoids orphan words at the end */
}

With solid typography, your site communicates professionalism. In the next lesson, we will explore colors and backgrounds to complete the visual aesthetics.

Practice

  1. Set up a system font stack: Apply a system font stack to the body and define a typographic scale for h1 through h3 using clamp() for fluid sizes.
  2. Create a .prose class: Build a .prose class with max-width: 65ch, line-height: 1.75, and vertical spacing using the * + * selector. Apply text-wrap: balance to headings.
  3. Load an optimized Google Font: Add a Google Font using <link rel="preconnect"> and font-display: swap. Compare the performance with and without preconnect in DevTools.
The 65-character rule
For comfortable reading, lines of text should not exceed 65-75 characters. Use max-width with the ch unit (width of the character 0) to achieve this: max-width: 65ch.
Font performance
Use font-display: swap so that text is immediately visible with a system font while the web font loads. This improves LCP (Largest Contentful Paint) and the user experience.
/* System font stack: fast and native */
body {
  font-family:
    system-ui,
    -apple-system,
    "Segoe UI",
    Roboto,
    "Helvetica Neue",
    Arial,
    sans-serif;
  font-size: 1rem;
  line-height: 1.6;
  color: #1a1a2e;
}

/* Modular typographic scale */
h1 { font-size: clamp(2rem, 5vw, 3.5rem); }
h2 { font-size: clamp(1.5rem, 4vw, 2.5rem); }
h3 { font-size: clamp(1.25rem, 3vw, 1.75rem); }

/* Google Font with font-display */
@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-var.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}

/* Readable paragraphs */
.prose {
  max-width: 65ch;
  font-size: 1.125rem;
  line-height: 1.75;
  letter-spacing: -0.01em;
}

.prose > * + * {
  margin-block-start: 1.25em;
}