Skip to content

Web Typography — Fonts & Text Styling Guide

DodaTech Updated 2026-06-21 8 min read

In this tutorial, you'll learn about Web Typography. We cover key concepts, practical examples, and best practices.

Web typography is the art and technique of arranging text on a website — choosing fonts, sizing, spacing, and layout to make content readable, accessible, and visually appealing.

What You'll Learn

  • Font formats, loading strategies, and performance trade-offs
  • Typographic scale, line-height, and responsive sizing
  • Accessibility best practices for readable web text

Why It Matters

95% of web content is text. Good typography improves readability by 30% and directly impacts user engagement. Bad typography — tiny fonts, poor contrast, or slow-loading custom fonts — drives users away and hurts Accessibility scores.

Real-world use: Doda Browser renders thousands of different fonts from websites worldwide. Durga Antivirus Pro scans web pages for malicious content — clear typography in scan reports helps users understand threats quickly. The DodaTech learning platform uses a carefully chosen type scale to keep tutorials readable across devices.

Typography Foundations

flowchart LR
  A[Font Selection] --> B[Font Loading
& Formats] B --> C[Type Scale
& Sizing] C --> D[Line-height
& Spacing] D --> E[Responsive
Typography] E --> F[Accessibility
& Contrast] style A fill:#f90,color:#fff style C fill:#4af,color:#fff style E fill:#4a4,color:#fff style F fill:#f4a,color:#fff

Font Formats & Performance

Format Browser Support Size (vs TTF) Best For
WOFF2 96%+ 30–50% smaller Modern sites — use this by default
WOFF 98%+ 15–30% smaller Fallback for older browsers
TTF/OTF 99%+ Full size Legacy support, desktop use
EOT IE only Full size Internet Explorer (deprecated)

Variable Fonts

Variable fonts pack multiple weights and styles into a single file by using interpolation axes. One variable font file can replace 10–20 individual weights, reducing font-related page weight by 60–80%.

Example axes: wght (weight 100–900), wdth (width 75–125%), slnt (slant), ital (italic)

Implementing Web Typography

Example 1: Loading Fonts with font-display: swap

/* Load Inter variable font with optimal settings */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/Inter.woff2') format('woff2');
  font-display: swap;
  font-weight: 100 900;
  font-style: normal;
}

/* Preload critical font */
<link rel="preload" href="/fonts/Inter.woff2" as="font" type="font/woff2" crossorigin>

/* Fallback font stack */
body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  color: #1a1a1a;
}

Expected output: The page renders immediately with a system font fallback (Segoe UI on Windows, Roboto on Android, system sans-serif on Mac). Once Inter.woff2 loads (typically within 200ms on a fast connection), the text swaps to Inter. The font-display: swap prevents invisible text while the font loads.

Example 2: Responsive Type Scale with CSS Clamp

/* Fluid typography scale using clamp() */
:root {
  --text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
  --text-sm: clamp(0.875rem, 0.8rem + 0.3vw, 1rem);
  --text-base: clamp(1rem, 0.9rem + 0.4vw, 1.125rem);
  --text-lg: clamp(1.125rem, 1rem + 0.5vw, 1.375rem);
  --text-xl: clamp(1.25rem, 1.1rem + 0.8vw, 1.75rem);
  --text-2xl: clamp(1.5rem, 1.2rem + 1.2vw, 2.25rem);
  --text-3xl: clamp(1.875rem, 1.4rem + 1.8vw, 3rem);
}

h1 { font-size: var(--text-3xl); line-height: 1.1; }
h2 { font-size: var(--text-2xl); line-height: 1.2; }
h3 { font-size: var(--text-xl); line-height: 1.3; }
p  { font-size: var(--text-base); line-height: 1.6; }

/* Example of clamping in action */
.hero-title {
  /* At 320px: ~28px. At 1440px: ~48px. No media queries needed. */
  font-size: clamp(1.75rem, 1.2rem + 2.5vw, 3rem);
  font-weight: 800;
  letter-spacing: -0.02em;
}

Expected output: On a 375px phone, h1 renders at 30px; on a 1440px desktop, it renders at 48px. No breakpoints needed. The text scales smoothly between those sizes. The line-height decreases for larger headings (1.1) and increases for body text (1.6), following typographic best practices.

Example 3: Full Typography System with CSS Custom Properties

/* Complete typography system */
:root {
  /* Font families */
  --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-serif: 'Merriweather', Georgia, 'Times New Roman', serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'Consolas', monospace;

  /* Font weights */
  --weight-normal: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;

  /* Letter spacing */
  --tracking-tight: -0.025em;
  --tracking-normal: 0;
  --tracking-wide: 0.05em;

  /* Line heights */
  --leading-none: 1;
  --leading-tight: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.625;
  --leading-loose: 2;

  /* Max line length for readability */
  --max-readable: 65ch;
}

/* Apply to elements */
article {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-relaxed);
  max-width: var(--max-readable);
  color: #1a1a1a;
}

code, pre {
  font-family: var(--font-mono);
  font-size: 0.9em;
  background: #f4f4f4;
  padding: 0.2em 0.4em;
  border-radius: 3px;
}

pre {
  padding: 1em;
  overflow-x: auto;
  line-height: var(--leading-normal);
}

/* Accessible text contrast */
.light-text {
  color: #6b7280; /* Gray-500: 4.6:1 contrast on white — WCAG AA */
}
.dark-text {
  color: #111827; /* Gray-900: 15:1 contrast on white — exceeds AAA */
}

Expected output: The article body renders in Inter at 16–18px with comfortable line spacing. Code snippets use a monospace font with subtle background highlight. The max-width: 65ch ensures lines don't exceed 65 characters, the optimal length for comfortable reading. Disabled or low-contrast text meets WCAG AA standards (at least 4.5:1 ratio).

Typography Accessibility Rules

Rule Why Implementation
Minimum 16px body text Smaller text forces users to zoom, breaks layouts font-size: clamp(1rem, 0.9rem + 0.4vw, 1.125rem)
Line spacing 1.5× for body text Improves readability and scanability line-height: 1.5 minimum for body
Max line length 65–75 characters Longer lines are hard to read max-width: 65ch on text containers
Contrast ratio at least 4.5:1 WCAG AA requirement for normal text Check with tools like WebAIM Contrast Checker
Don't use colour alone to convey meaning Screen readers can't see colour Use icons, underlines, and patterns too
Allow user font scaling Respect browser font size settings Use rem not px for font-size
Focus indicators visible Keyboard navigation requires visible focus outline: 2px solid #2563eb on :focus-visible

Common Typography Errors

  1. Using px for font-sizes — Pixels don't scale with browser settings. Users who set their browser to 150% font size get broken layouts. Always use rem for font sizes. 1rem = 16px by default but respects user preferences.

  2. FOUT (Flash of Unstyled Text) or FOIT (Flash of Invisible Text) — Custom fonts block rendering by default. Use font-display: swap to show fallback text immediately and swap when the custom font loads.

  3. Loading too many font weights — Loading 5+ weights of a font adds 200KB+ to page weight. Use variable fonts or limit to 2–3 weights (regular, medium, bold). Lazy-load additional weights if needed.

  4. Poor contrast on body text — Light gray text (#999) on white background fails WCAG contrast checks. Use #1a1a1a for dark text on light backgrounds. Check with the WebAIM contrast checker.

  5. Line length too long — Text that spans the full width of a 1920px monitor is exhausting to read. Set max-width: 65ch on article containers. One ch unit equals one character width.

  6. Not defining fallback fonts — If a custom font fails to load and there's no fallback, the browser uses its default (often Times New Roman). Always provide a proper font stack ending with a generic family like sans-serif or serif.

Frequently Asked Questions

What is the best font size for body text on the web?

16px (1rem) is the standard minimum for comfortable reading on desktop. On mobile, 16px prevents iOS from zooming in on form inputs. Use clamp(1rem, 0.9rem + 0.4vw, 1.125rem) for fluid sizing that scales between 16px and 18px based on viewport width.

How many font families should I use on a single website?

Limit yourself to two font families maximum — one for headings (often a bold, distinctive typeface) and one for body text (a highly readable typeface). Using more than two creates visual clutter and increases page weight. A single-family approach (same font, different weights) works well for minimal designs.

What is the difference between serif and sans-serif fonts for the web?

Serif fonts have small decorative strokes at the end of characters and are traditionally used for print body text. Sans-serif fonts lack these strokes and are generally considered more readable on screens, especially at small sizes. However, well-designed screen serifs like Merriweather or Georgia work well for long-form reading online.

Practice Questions

  1. What is the difference between font-display: swap and font-display: block? swap shows fallback text immediately and swaps when the custom font loads. block hides text for up to 3 seconds while waiting for the custom font, causing invisible text.

  2. Why should you use rem instead of px for font sizes? rem units respect the user's browser font size setting. If a user sets 150% font size, 1rem becomes 24px instead of 16px. px values ignore this setting, making text harder to read for users who need larger fonts.

  3. What is a variable font and why does it improve performance? A variable font packs multiple weights and widths into a single file using interpolation axes. One variable font can replace 10+ individual weight files, reducing font-related payload by 60–80%.

  4. What is the optimal line length for readability? 50–75 characters per line. CSS ch units measure character width, so max-width: 65ch is the standard target for body text containers.

  5. How does clamp() help with responsive typography? clamp(MIN, PREFERRED, MAX) sets a fluid size that scales between a minimum and maximum based on the viewport. It eliminates the need for multiple media queries for font sizes.

Challenge

Build a typography system for DodaTech tutorials: set up a variable font (Inter or similar), create a fluid type scale with clamp() for all heading levels (h1–h6) and body text, implement a monospace font for code blocks, ensure WCAG AA contrast for all text, and test readability on mobile (375px) and desktop (1440px). Measure the font loading performance with Lighthouse.

Real-World Task

Durga Antivirus Pro's scan report page uses 14px text with line-height: 1.2 and no fallback fonts — users report eye strain after reading reports. Redesign the typography: set body text to 16px minimum with line-height: 1.6, add a proper font stack, use font-display: swap for the primary font, set max-width: 70ch for report content, and ensure all threat severity labels use colour-plus-icon indicators for colour-blind users.


Related: CSS Preprocessors | Related: Responsive Design Guide | Related: Web Accessibility Guide

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro