Build responsive type systems that adapt to reading context across devices — combining fluid scaling, container queries, and intentional breakpoint overrides for optimal legibility at every size.
Responsive typography is not just about scaling font sizes. It is about maintaining optimal reading conditions — the right measure, the right line height, the right spacing — across every context from a 320px phone to a 2560px monitor. This lesson covers the full picture: fluid scaling where appropriate, explicit overrides where necessary, and the CSS properties beyond font-size that determine whether text is actually comfortable to read.
Fluid clamp() scaling works perfectly for font sizes that should vary proportionally with viewport width — body text, headings, display titles. But some typographic decisions are binary, not continuous: switching from a single-column mobile layout to a two-column desktop layout changes the optimal measure entirely. At 320px, a single-column layout with 45ch measure is correct; at 1200px with two columns, each column might be 35ch — a smaller measure than on mobile despite the larger screen. These are not font-size changes; they are layout-driven typography changes that require explicit breakpoint overrides. Rule: use fluid scaling for font sizes, use breakpoints for measure, column count, line-height shifts, and typographic layout changes.
1/* Fluid font size — no breakpoints needed */
2body {
3 font-size: clamp(1rem, 0.92rem + 0.4vw, 1.125rem);
4}
5
6/* Measure and layout — breakpoints ARE appropriate */
7article {
8 max-width: 90vw; /* Mobile: full width */
9 margin-inline: auto;
10 line-height: 1.7;
11}
12
13@media (min-width: 600px) {
14 article {
15 max-width: 65ch; /* Switch to character-based measure */
16 line-height: 1.65;
17 }
18}
19
20@media (min-width: 1200px) {
21 article {
22 max-width: 70ch;
23 line-height: 1.6; /* Slightly tighter at larger sizes */
24 }
25}A card component might appear in a 3-column grid on desktop (each card ~300px wide) and as a full-width item on mobile (360px wide). With viewport-based font sizes, the heading in the wide card and the heading in the narrow card are the same size — but the narrow card has much less horizontal space. Container queries let the component's typography respond to its own width, not the viewport. This is the correct responsive model for reusable components.
1/* Establish the containment context */
2.card {
3 container-type: inline-size;
4 padding: 1.5rem;
5 border-radius: 0.5rem;
6}
7
8/* Typography defaults (narrowest) */
9.card-title {
10 font-size: 1.125rem;
11 line-height: 1.3;
12 font-weight: 700;
13}
14
15.card-body {
16 font-size: 0.9375rem;
17 line-height: 1.55;
18}
19
20/* Wider card context: increase type scale */
21@container (min-width: 400px) {
22 .card-title {
23 font-size: 1.333rem;
24 line-height: 1.25;
25 }
26 .card-body {
27 font-size: 1rem;
28 line-height: 1.6;
29 }
30}
31
32@container (min-width: 600px) {
33 .card-title {
34 font-size: 1.5rem;
35 letter-spacing: -0.01em;
36 }
37}Line height (leading) and measure (line length) are as important as font size for readability. The optimal measure for body text is 45–75 characters per line (45–75ch). Below 45ch lines feel choppy; above 75ch the eye struggles to track back to the start of the next line. Line height should be inversely proportional to measure: short lines (narrow columns) need more line height to help the eye navigate vertically; long lines (wide columns) need less. A 45ch measure wants line-height around 1.8; a 75ch measure wants around 1.55. As font size increases (display headings), line height should decrease — large text with 1.6 line height has enormous gaps between lines. Display headings often look best at 1.0–1.1 line height.
1/* Responsive line height and measure system */
2:root {
3 /* Measure: character-based — adapts to font automatically */
4 --measure-narrow: 45ch;
5 --measure-body: 65ch;
6 --measure-wide: 80ch;
7}
8
9body {
10 font-size: clamp(1rem, 0.92rem + 0.4vw, 1.125rem);
11 line-height: 1.65;
12 max-width: var(--measure-body);
13}
14
15/* Headings: tighter line height as size increases */
16h1 { line-height: 1.05; letter-spacing: -0.025em; }
17h2 { line-height: 1.15; letter-spacing: -0.015em; }
18h3 { line-height: 1.25; letter-spacing: -0.01em; }
19h4 { line-height: 1.35; }
20
21/* Narrow sidebar: looser line height, shorter measure */
22.sidebar {
23 max-width: var(--measure-narrow);
24 line-height: 1.75; /* Compensate for short lines */
25 font-size: 0.9375rem;
26}A reading mode layout (article, blog post, documentation) is the highest-stakes responsive typography context. At mobile widths it needs: moderate font size, loose line height, near-full-width measure. At tablet widths: slightly larger font, slightly tighter line height, constrained measure. At desktop widths: maximum font size, tightest line height, maximum measure with comfortable margins. The relationship between all three properties — size, measure, leading — should shift together, not independently.
1/* Complete responsive reading layout */
2.reading-layout {
3 /* Fluid font — scales continuously */
4 font-size: clamp(1rem, 0.92rem + 0.4vw, 1.2rem);
5
6 /* Measure: constrained to readable range at all widths */
7 width: min(65ch, 90vw);
8 margin-inline: auto;
9
10 /* Line height: fluid, inversely related to font size */
11 line-height: clamp(1.55, 1.7 - 0.1vw, 1.75);
12
13 /* Paragraph spacing: proportional to line height */
14 p { margin-block-end: 1.5em; }
15
16 /* Drop cap for first paragraph */
17 > p:first-of-type::first-letter {
18 font-size: 3.5em;
19 line-height: 0.85;
20 float: inline-start;
21 margin-inline-end: 0.1em;
22 font-weight: 700;
23 }
24}
25
26/* Print: fixed size, wider measure, tighter leading */
27@media print {
28 .reading-layout {
29 font-size: 11pt;
30 line-height: 1.45;
31 max-width: none;
32 width: 100%;
33 }
34}Do not rely solely on desktop browser resizing. Test at real device conditions: Chrome DevTools Device Toolbar emulates exact device widths and pixel densities. Key widths to test: 320px (iPhone SE), 375px (iPhone 14), 390px (iPhone 14 Pro), 768px (iPad portrait), 1024px (iPad landscape / small laptop), 1280px, 1440px, 1920px. For each, verify: body text is at least 16px, measure is in the 45–75ch range, headings have appropriate contrast with body, line height is not too tight or too loose. Also test with browser text zoom at 150% and 200% — WCAG 1.4.4 requires no content loss. Test with the user-agent stylesheet disabled to confirm your type system works without browser defaults.
1/* Debug helper: visualise measure */
2.reading-layout {
3 /* Temporarily add this to see the 45ch and 75ch markers */
4 outline: 2px solid red;
5}
6
7/* Debug: show actual rendered font size */
8/* In browser console: */
9/* getComputedStyle(document.body).fontSize */
10
11/* Progressive enhancement: larger type for high-DPI screens */
12@media (min-resolution: 2dppx) {
13 body {
14 /* High-DPI screens render text crisper — */
15 /* slightly lighter weight reads better */
16 -webkit-font-smoothing: antialiased;
17 -moz-osx-font-smoothing: grayscale;
18 }
19}A complete responsive article layout with fluid type, container queries, and optimal reading conditions at every viewport.
Next lesson covers OpenType features in CSS — ligatures, small caps, tabular figures, and oldstyle numerals — the fine typographic controls that separate professional type from default rendering.
Your progress is saved in this browser, on this device only.