Home/Learn/HTML, CSS & Responsive/Colour and Typography

Colour and Typography

Intermediate
CSS Fundamentals

Readable text is mostly four decisions — size, line height, line length and contrast — and web fonts are the most common cause of a slow, shifting page.

Overview

Typography is where a page is judged before anything is read, and the defaults are close to the right answer more often than people assume. The four settings that decide readability are the size, the space between lines, the length of a line, and the contrast against the background — and each has a known target. Web fonts then introduce a performance problem: a font file that arrives late either hides the text or swaps it, and both are visible. Colour, meanwhile, has a hard accessibility floor that a designer's preference does not override.

Readable Text

The four numbers, and their targets.

Size, line-height, measure
body {
  font-size: 1rem;              /* 16px minimum for body text. Never 12px. */
  line-height: 1.5;             /* unitless, so it scales with font-size */
  max-width: 65ch;              /* 45-75 characters per line */
}
h1 { font-size: 2.5rem; line-height: 1.1 }   /* tighter for large text */

/* line-height with a unit does NOT scale for children — this is why
   unitless is the rule: */
body { line-height: 1.5 }       /* correct */
body { line-height: 24px }      /* a 32px heading also gets 24px */

/* A modular scale keeps sizes coherent rather than arbitrary */
--step-0: 1rem; --step-1: 1.25rem; --step-2: 1.563rem; --step-3: 1.953rem;

/* Other text properties worth knowing */
letter-spacing: .05em           /* only for UPPERCASE labels */
text-wrap: balance              /* even heading line lengths */
text-wrap: pretty               /* no single-word last line in a paragraph */
hyphens: auto
font-variant-numeric: tabular-nums   /* digits align in columns */

/* Do not justify text on the web — it creates rivers of whitespace
   without the hyphenation engine print has. */

Loading Fonts

The two failure modes — invisible text, and a visible swap.

font-display, preload, metric-matched fallback
@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter.woff2') format('woff2');
  font-display: swap;           /* show the fallback, swap when ready */
  font-weight: 100 900;         /* one variable font, every weight */
}

/* font-display options:
     block  — invisible text for up to 3s (FOIT). Avoid.
     swap   — fallback immediately, then swap (FOUT). The usual choice.
     fallback / optional — swap only if it arrives fast. optional is
                           best for a body font on a slow connection. */

<link rel="preload" href="/fonts/inter.woff2" as="font"
      type="font/woff2" crossorigin>
/* crossorigin is REQUIRED on a font preload even for same-origin,
   or the browser downloads the file twice. */

/* Reduce the swap's layout shift by matching the fallback's metrics */
@font-face {
  font-family: 'Inter Fallback';
  src: local('Arial');
  size-adjust: 107%; ascent-override: 90%;
}
body { font-family: 'Inter', 'Inter Fallback', sans-serif }

/* The zero-cost option: the system stack. No request, no shift. */
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;

Colour and Contrast

The formats, and the ratios that are not negotiable.

oklch, 4.5:1, and never colour alone
color: #1a1a1a
color: rgb(26 26 26 / 80%)         /* modern space-separated syntax */
color: hsl(220 90% 56%)            /* hue, saturation, lightness —
                                      easy to derive a whole palette */
color: oklch(60% 0.15 250)         /* perceptually uniform: equal
                                      lightness looks equally light */
color: color-mix(in oklch, var(--accent) 20%, white)

/* WCAG AA contrast minimums — a hard floor, not a preference:
     body text (< 18.66px or < 24px bold)   4.5 : 1
     large text                             3   : 1
     UI borders, icons, focus rings         3   : 1
   Grey placeholder text on white is the most common failure. */

/* Never use colour alone to carry meaning — about 1 in 12 men has
   a colour vision deficiency: */
<span class="error">✕ Failed</span>      /* icon + text + colour */

/* currentColor inherits the text colour — one icon, every theme */
.icon { fill: currentColor }

/* Respect the user's system preference */
@media (prefers-color-scheme: dark) { :root { --bg: #101010 } }
@media (prefers-reduced-motion: reduce) { * { animation: none !important } }

Key Points to Remember

  • 1Body text at 1rem, line-height 1.5 unitless, and a measure of 45–75 characters cover most of readability
  • 2A line-height with units does not scale for children, which is why unitless is the rule
  • 3font-display: swap avoids invisible text, and a font preload needs crossorigin even same-origin
  • 4WCAG AA requires 4.5:1 for body text and 3:1 for large text and UI elements — it is a floor, not a preference
  • 5Never encode meaning in colour alone, and respect prefers-color-scheme and prefers-reduced-motion

Interview Questions

Sign in to ask Aria
1

Why should line-height be unitless?

Medium
2

What does font-display: swap do, and what is the alternative failure mode?

Medium
3

What contrast ratio does normal body text need, and why does it matter?

Medium

Ask Aria about Colour and Typography

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…