Copy the CSS from a font pairing tool, paste it into your stylesheet, and you can ship a
fallback stack that contradicts your own font choice. Our font pairer has this exact bug
in its copied output. This article walks through the four default pairs the tool ships,
the rules behind them, and the three fixes to make before the snippet reaches production.
What the tool actually contains
The pairer offers 18 families: Inter, Roboto, Open Sans, Lato, Montserrat, Poppins,
Raleway, Oswald, Playfair Display, Merriweather, Source Sans 3, Nunito, PT Sans, Work
Sans, Lora, Crimson Text, Libre Baskerville, and Cormorant. Every one loads from Google
Fonts when selected, through a single stylesheet link the component injects into the
page head with display=swap.
The four suggested pairings are:
1. Playfair Display headings over Source Sans 3 body text.
2. Montserrat headings over Merriweather body text.
3. Oswald headings over Lato body text.
4. Raleway headings over Roboto body text.
These four are not decoration. Each one encodes a different contrast rule, and together
they cover the situations most projects face.
The pairing rules the defaults encode
Read the four pairs as condition-action rules and you can generate your own with the
same logic:
1. If your page has long body text, pair across classifications. Put one serif with one
sans. Playfair Display over Source Sans 3 does this with a serif heading. Montserrat
over Merriweather does it inverted, with the serif carrying the body.
2. If both fonts must be sans, create contrast through width and weight. Oswald is a
condensed face against the normal-width Lato, so headings compress while paragraphs
stay open.
3. If two fonts feel too similar at the same size, keep both and assign them strictly by
role. Raleway takes headings, Roboto takes body, and neither drifts into the other
job.
4. If you need a third face, stop. The tool previews two roles only because two roles
cover heading and body. Add a third face for pull quotes at most, and borrow weights
from your existing pair first.
One further rule comes from the preview itself. The tool renders its sample paragraph
in a pangram for each of its 12 interface languages, because a pairing decision made on lorem-style filler hides how
the pair handles accented characters. Test any candidate
pair on real text with your actual diacritics before committing.
What the generated CSS contains
Select Playfair Display and Source Sans 3 and the copy panel produces this structure:
/* Heading */
font-family: 'Playfair Display', sans-serif;
/* Body */
font-family: 'Source Sans 3', sans-serif;
/* Google Fonts Import */
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Source+Sans+3:wght@400;700&display=swap');
Three properties of this snippet need attention before you ship it.
Three fixes before the snippet reaches production
Fix the fallback family first. The snippet appends sans-serif as the fallback for both
roles, including the Playfair Display heading. When the web font fails or swaps in late,
a serif design falls back to a sans face and the layout shifts visually. Change the
heading fallback to serif whenever the heading font is a serif. The live preview in the
tool does not expose this because it swaps between two loaded fonts, so the bug only
appears in the copied output, on slow connections, or when Google Fonts is blocked.
Fix the loading strategy second. The snippet uses two separate @import statements.
CSS imports inside a stylesheet serialize font loading behind the stylesheet itself,
which is slower than a link element. The component knows this, it loads the preview
through a link element with display=swap, so copy that approach into your HTML head
instead of pasting the @import lines into your CSS.
Fix the weight set third. The imports request weights 400 and 700 only. If your design
uses a 500 or 600 weight, the browser synthesizes it or drops it, and both outcomes
degrade the pair. Add the weights you actually use to the import URL, or remove unused
ones and save the request.
A deterministic pairing workflow
1. Write down the two roles in your design and the real text each will carry.
2. Pick the body font first, from the families with open forms at 16px, and confirm it
on a paragraph of your own content, not a pangram.
3. Pick the heading font to contrast with the body on at least one axis: class, width,
or weight.
4. Load both in the pairer and read the preview at your real sizes.
5. Copy the CSS, replace the fallback family on any serif role, and rewrite the imports
as a single link element with your exact weights.
6. Check the result with fonts disabled in your browser. If the fallback still reads
correctly, the pair survives failure.
Checklist before you commit a pair
- One serif and one sans, or a deliberate width contrast between two sans faces.
- Two families total, weights borrowed before a third family is added.
- Fallback family matches the classification of each role.
- Fonts load through a link element with display=swap, not @import.
- Import requests exactly the weights your CSS uses.
- The pair was judged on your own text, including every diacritic you publish.
Font pairing fails in production for reasons the preview never shows: the wrong
fallback, a slow import chain, a missing weight. The four defaults in our tool give you
a working starting point for each contrast pattern, and the fixes above close the gap
between the copied snippet and something you would ship.
Which pair did you settle on, and what did your fallback look like with fonts blocked?
Preview your own candidates in the [font pairer](https://webrecast.com/en/font-pairer)
and check the copied CSS against the three fixes before it lands.