Font pairing without a type degree: the three decisions that matter
Most font-pairing advice is a list of pairs that worked for someone else. The useful version is a short procedure — decide contrast, decide the split, then set four numbers — and it gets you to a defensible answer in about ten minutes without knowing anything about type history.
2026-08-08 · 7 min read
Pairing two typefaces is three decisions, not a lookup: how much contrast you want between them, which parts of the page each one takes, and the four numbers you set afterwards. Get those and almost any competently drawn pair works. Skip them and the "perfect pairing" from a listicle still looks wrong on your page, because the pair was never the variable.
This matters more than it used to. Generic sans-on-sans — Inter over Inter, Geist over Geist — is one of the named tells of a machine-built site, near the bottom of the ranked list but present. It is also the easiest one to fix, because unlike layout it costs one decision and no refactor.
Decision 1: how much contrast
Two typefaces on a page have to look either clearly different or deliberately the same. The failure mode is the middle: two grotesques that are almost the same, which reads as a mistake rather than a choice.
Three usable settings:
- High contrast — a serif against a sans, or a display face against a text face. The heading has a voice; the body gets out of the way. This is where most of the character available to you lives, and it is the setting most default builds are missing.
- One family, two weights — a single superfamily doing both jobs, separated by weight and size rather than by shape. Quiet, hard to get wrong, and correct for dense product UI where a display face becomes noise.
- Same category, different personality — a geometric sans over a humanist sans, say. This is the one that needs a real eye. If you are unsure, you are in the failure zone; pick one of the other two.
Pick the setting before you pick the fonts. It turns an open question into a shortlist.
Decision 2: the split
Which parts of the page get the heading face is a decision people skip, and it is why a pairing that looked right in a specimen looks wrong in situ. A specimen shows one heading over one paragraph. Your page has an h1, three h2s, a nav, four buttons, a stat block, a table header and a caption — and every one of those is either "heading voice" or "body voice".
The default answer is: headings take the display face, everything else takes the text face. Two adjustments earn their keep:
- Numbers usually want the heading face. Big metrics, prices, stat values. They are display elements wearing a body class.
- Nav, buttons and labels usually want the body face, even though they are short. Setting them in the display face is what makes a page feel like a poster instead of a product.
If a pairing looks off and you cannot say why, it is usually the split, not the pair.
Decision 3: the four numbers
Two typefaces do not have the same metrics, so dropping one in over the other and leaving the CSS alone hands you a page where the heading is suddenly too big and the body too loose. Four numbers, in order:
- Size. A serif set at the same px as a sans usually reads smaller — its x-height is often lower. Expect to nudge one of the two.
- Weight. Display faces carry more colour per stroke; a heading that was 700 in a grotesque is often 600 in something with more character, and the 700 version looks shouty.
- Letter-spacing. Large text wants it tighter (a headline at default tracking looks loose); small uppercase text — labels, eyebrows, buttons — wants it looser. These are opposite adjustments, which is why a single global tracking value never works.
- Line height. The number that most affects whether a page feels readable and the one most often left at the framework default. Body copy at a comfortable measure usually wants 1.5–1.6; headings want to come down, often below 1.2.
Set them in that order. Size and weight change what the other two should be, so doing tracking first means doing it twice.
What "good" looks like when you are checking
One test, and it is not a specimen: put the pair on your longest heading over your real body copy, at the width your page actually is. Then check three things.
- Does the heading still read as one thing at that length, or does it break into two lines that fight?
- Is the body measure between roughly 45 and 75 characters a line? Wider than that and no font choice will save it.
- Do the small uppercase bits — labels, buttons, eyebrows — still read, or has the display face's personality made them mush?
If those three pass, the pairing is fine. There is no fourth test that a type designer would apply and you would not.
The shortest version
Decide the contrast setting. Decide the split. Set size, weight, tracking and leading, in that order. Check it on your longest heading at your real width.
That procedure is why the 102 pairings in the gallery are rendered as live specimens with real weights rather than listed as names — a pair you cannot see working is a pair you cannot judge. The extension applies any of them to your own page and puts the four numbers on sliders, so decision 3 takes about a minute instead of a build cycle. And when none of the 102 is right, the free-text search reaches all 1,800-odd Google Fonts — but it sits under the curated list on purpose, because a search box over 1,800 families is a way to spend an afternoon rather than make a decision.
Q: How many typefaces should a site use? A: Two is the working answer — one for headings, one for body — with a third only if you need a monospace for code. Three text faces is almost always a sign that the split (decision 2) was never made.
Q: Is a serif heading over a sans body safe? A: It is the highest-value default in the list, because it is where most of the available character lives and it is unmistakably a decision. It needs the four numbers set afterwards; the metrics of a serif and a sans rarely match.
Q: Do I need to self-host the fonts? A: Not to decide. Pick the pairing first with whatever is fastest to try, and treat hosting as a separate performance decision once the choice is made — swapping the delivery method later costs nothing, and swapping the typeface after the page is built costs a lot.