Choosing Fonts for Web Design: A Practical Guide
Choose web fonts by the job each one has to do. Most sites need one family for body and interface text and, at most, a second for headings. Pick the body face first, test it with real copy at the size readers will see (browsers usually default to 16px), confirm the weights, figures and languages you need, then check the license before anything ships.
Everything else in this guide expands those steps: what to look for in a specimen, how pairing actually works, how font choice interacts with accessibility rules, what each font costs to load, and how to document the decision so the next person on the project does not repeat it.
Start with jobs, not with fonts
Font browsing is enjoyable, which is why it tends to happen first. It works better as the third step. Before opening a catalog, write down every role text plays in the product. A typical marketing site plus app has these:
| Job | Where it appears | What the font must do well | Typical candidates |
|---|---|---|---|
| Reading | Articles, product descriptions, help pages | Stay comfortable over several paragraphs | Text serif or humanist sans |
| Interface | Buttons, labels, menus, form fields | Stay crisp and unambiguous at small sizes | Neutral or humanist sans, system UI stack |
| Headings | Page titles, section heads | Carry the brand voice at large sizes | Display serif, geometric or grotesque sans |
| Data | Tables, prices, dashboards | Align digits in columns, distinguish 0 from O | Sans with tabular figures, or a monospace |
| Code | Snippets, API docs | Fixed-width alignment | Monospace |
Two observations come out of this table every time. First, reading and interface jobs usually fall to the same family, because both need the same qualities. Second, the data job is often solved by a font feature rather than a third font: many text families include tabular figures you can switch on in CSS.
So the default plan is one family covering reading, interface and data, plus an optional display family for headings. A third family needs a reason you can state in a sentence, such as "the docs section shows code."
Font categories, briefly
Category labels are shorthand. They help narrow a search but they do not predict quality.
- Serif faces have small finishing strokes. Old-style designs (Garamond lineage) feel bookish; transitional and modern designs have more contrast between thick and thin strokes; slab serifs have heavy, block-like serifs that hold up well in headings.
- Sans-serif faces drop the serifs. Grotesque and neo-grotesque designs (the Helvetica lineage) are neutral; geometric designs build letters from circles and straight lines; humanist designs keep traces of the pen and tend to have more open shapes.
- Monospace faces give every character the same advance width. Useful for code and sometimes for numbers.
- Display faces are drawn for large sizes and short runs of text. Their details often disappear or clash below heading size.
- Script and handwriting faces imitate pen strokes. They suit a word or two, rarely a sentence.
For the reading job, serif and sans are both valid on modern screens. The choice is about voice and about the specific design, not a rule that one category is more legible than the other.
Reading a specimen like a reviewer
A specimen page is a sales sheet. Set your own text and check the following, in this order, because each check is quicker than the next and eliminates more candidates.
1. Ambiguous characters
Type Il1 O0 rn m cl d at your smallest interface size. Uppercase I, lowercase l and the digit 1 should be distinguishable; so should O and 0. "rn" should not read as "m" and "cl" should not read as "d". This matters most in passwords, codes, order numbers and email addresses. Some fonts include a slashed or dotted zero you can enable with font-variant-numeric: slashed-zero, which MDN maps to the OpenType zero feature.
2. x-height and apertures
The x-height is the height of lowercase letters relative to capitals. A taller x-height makes text look larger at the same font size. Apertures are the openings in letters like c, e, a and s. Open apertures keep those letters distinct when rendering gets soft at small sizes; tight apertures can make e look like o. Compare candidates side by side at 14px and 16px on the screens your audience uses, not only on a high-density laptop display.
3. Weights you will actually use
List the weights the design calls for, usually regular, a medium or semibold for emphasis and labels, and bold. Check that each exists as a designed style. If a weight is missing, the browser may synthesize it: MDN's font-synthesis page explains that browsers can fake bold and italic when the family lacks them, and faked bold looks smeared. A variable font covering a weight range solves this in one file; our variable fonts guide explains how to tell whether a family's range covers your needs.
4. Figures
For prices, tables and dashboards you want tabular figures, where every digit has the same width so columns line up. MDN's font-variant-numeric page maps tabular-nums to the OpenType tnum feature and lining-nums to lnum. Not every font has them. Test with:
.data-table td { font-variant-numeric: tabular-nums lining-nums; }
If a column of prices still wobbles, the font lacks the feature, and the data job may need a different family.
5. Language coverage
List every language the site serves now or plans to serve within the year. Check accented Latin characters (for example ő and ű for Hungarian, ș and ț for Romanian), and any non-Latin scripts. A family with no Vietnamese or Cyrillic support is fine for an English-only site and a blocker for a site launching in Kyiv.
6. Italics
Check whether the family has true italics or only obliques, and whether you need them. Body copy usually needs italics for titles and emphasis. A family without them will get synthesized italics wherever an <em> appears.
Pairing that holds up
Pairing advice is often stated as taste. The rules below are mechanical, which makes them easier to apply and to defend in a design review.
Clear contrast or no pairing. Two families on one page should differ obviously in structure: serif with sans, or a high-contrast display face with a low-contrast text face. Two neo-grotesque sans faces side by side read as a mistake, because the eye notices that something changed without being able to say what.
Assign the jobs, then stick to them. Headings get the display family; everything else gets the text family. Mixing them inside components (a display-face button label on one page, text-face on another) erodes the system faster than any pairing choice.
Match proportions where they meet. When a heading sits directly above body text, very different x-heights make the transition jarring. Compare the two faces at their real sizes, heading above paragraph, rather than in separate specimen windows.
Check both families at the extremes. Set the smallest caption and the largest hero size in each. Some display faces become fussy at 24px; some text faces look flat at 72px.
When unsure, use one family. A single well-drawn family with a good weight range can create hierarchy through size, weight and spacing alone. Many product interfaces never need a second family.
| Pairing pattern | When it works | Watch for |
|---|---|---|
| One family, several weights | Product UI, dashboards, documentation | Headings that look like bold body text; add size and spacing contrast |
| Serif headings, sans body | Editorial, finance, institutions | Serif with very fine hairlines breaking up at mid sizes |
| Sans headings, serif body | Long-form reading sites | Body serif that feels too small next to a heavy sans |
| Display face for hero only, text family elsewhere | Brand campaigns, landing pages | Display face creeping into navigation or buttons |
| Superfamily (matching serif and sans from one designer) | Large systems that need both voices | Licensing both members if the family is commercial |
For display faces specifically, including how they behave in nav bars and small badges, see our guide to display fonts for brands.
Size, spacing and the accessibility rules that touch fonts
Accessibility standards do not ban any font, and WCAG 2.2 sets no minimum font size. They do constrain how your type must behave.
Default size. MDN's font-size page notes that the default browser font size "is usually 16px" and that defining font sizes in px "is not accessible" because some browsers then ignore the user's own size preference. Setting body text in rem keeps it tied to that preference. The arithmetic and the edge cases are in our px vs rem vs em guide.
Text spacing. WCAG 2.2 Success Criterion 1.4.12 (Level AA) requires that content still works when a user overrides spacing to at least these values: line height 1.5 times the font size, paragraph spacing 2 times, letter spacing 0.12 times and word spacing 0.16 times. The criterion is about not breaking (no clipped or overlapping text), but it also gives a sensible starting point. A body line height around 1.5 rarely needs the user to change anything.
That has a consequence for font choice. A face with a very tall x-height or long ascenders and descenders can look cramped at 1.5 and may need more. A face with a small x-height can look gappy. Set a paragraph at your intended line height before committing.
Contrast. Thin weights reduce the effective darkness of text even when the color value passes. If a design uses a light weight for body or captions, test it at real size against the real background, and compare it with a regular weight. Our WCAG 2.2 AA guide lists the contrast thresholds and how they apply to text of different sizes.
All caps and letter spacing. Short labels in capitals often need a little positive letter spacing to stay readable. Long passages in capitals are harder to read in any font, so keep them to a few words.
What fonts cost to load
Every family and weight is a file the browser downloads before it can show text in that face. The questions to ask:
Which format? WOFF2 is a W3C Recommendation (the current version is dated 8 August 2024) and is the format to serve. Our font formats guide explains why the TTF or OTF a foundry delivers needs converting first.
How many files? Count the styles that load on your most visited page. Regular, italic, semibold and bold of one family is four files. A display family adds more. Every file is a request and a potential layout shift when it arrives.
What happens while loading? The font-display descriptor controls the wait. MDN defines swap as "an extremely small block period and an infinite swap period" (fallback text shows almost at once and the web font replaces it whenever it arrives), optional as an extremely small block period and "no swap period" (if the font is not ready almost immediately, the fallback stays for that page view), and block as "a short block period and an infinite swap period" (text is invisible briefly). Body text usually suits swap; a decorative display face that is not essential may suit optional.
Should anything be preloaded? Preloading the one or two files used above the fold lets the browser fetch them earlier. MDN notes that font preloads require the crossorigin attribute "even when the fetch is not cross-origin", because fonts are fetched in anonymous-mode CORS:
<link rel="preload" href="/assets/fonts/body-regular.woff2" as="font" type="font/woff2" crossorigin>
MDN's point is that the preload's CORS mode has to match the request the stylesheet later makes for the font. If the two do not match, the preload does not serve that request, so check the network panel for the font appearing twice after adding a preload.
Can the files be smaller? Subsetting removes characters you do not need. Google's CSS API documentation says a text= request can, in some cases, cut a font file by up to 90%. For self-hosted files, subset to the languages you actually serve. If the family is under the SIL Open Font License, note that the OFL FAQ treats subsetting as a modification: it is allowed, but a subset normally cannot keep a Reserved Font Name.
When fonts fail to appear or flash in unexpected ways, the web fonts not loading guide walks through the checks in order.
If your shortlist includes a font that is paid and widely imitated, such as Helvetica, the licence and metric questions for the free look-alikes are answered in free Helvetica alternatives.
Licenses: check before the font enters the design file
A font you cannot legally ship costs more to replace late than early, because by then it is in mockups, slides and the logo. The basics:
- Google Fonts. The project's GitHub repository says most fonts in the collection use the SIL Open Font License 1.1, some use the Apache License 2.0, and the Ubuntu fonts use the Ubuntu Font License 1.0. It also says "It is important to always read the license for every font that you use." Each family's license file sits in its folder. Our Google Fonts commercial use and self-hosting guide covers the duties family by family.
- SIL OFL in practice. The license permits using, embedding, modifying and redistributing the font; the main restriction is that the font may not be sold by itself. The OFL FAQ confirms that serving the font with
@font-faceis a form of distribution the license allows, that designs and documents made with the font are not themselves under the OFL, and that logos made with an OFL font remain your own artwork. - Commercial foundries. Desktop, web, app and document-embedding rights are frequently priced as separate licenses, and web licenses may be limited by pageviews or domains. Read the end user license agreement for each use the project needs.
- Aggregator sites. A "free download" button on a site that did not make the font says nothing reliable about its license. Get fonts from the designer, the foundry or a catalog that publishes the license with each family.
The full decision process, including red flags in license files and a pre-shipment checklist, is in our commercial font licensing guide. If the font is for a logo, the free fonts for logos guide covers trademark and modification questions specific to wordmarks.
A scoring sheet for the shortlist
Once you have three or four candidates per job, score them on the same sheet. This keeps the decision explainable and stops the most recently viewed font from winning by default. Score each criterion 0 (fails), 1 (acceptable) or 2 (strong). Any 0 on a "must" criterion eliminates the font regardless of total.
| Criterion | Must or nice | How to test |
|---|---|---|
| Ambiguous characters distinct (Il1, O0) | Must for interface and data | Type the test string at smallest UI size |
| Required weights exist as designed styles | Must | Check the family's style list or variable range |
| Required languages covered | Must | Type sample text in every target language |
| License covers every planned use (web, app, PDF, logo) | Must | Read the license file, not the download page |
| Tabular figures available | Must for data job | Apply tabular-nums, check a price column |
| Readable paragraph at body size and 1.5 line height | Must for reading job | Set three paragraphs of real copy |
| True italics | Nice | Set emphasized words in body text |
| Fits brand voice | Nice | Review headline and paragraph with stakeholders |
| Total file weight for styles on the home page | Nice | Sum WOFF2 sizes after subsetting |
Worked example: scoring for a fintech landing page
This is an illustration of the method with stated inputs, not a record of a client project or a test we ran.
Inputs. A fictional personal-finance startup needs a marketing site and a web app. Content is English only. Jobs: headings, reading and interface text, and data (balances, transaction tables). The brand brief asks for "established, not playful." Budget: free fonts only, so candidates come from open-licensed catalogs.
Shortlist. For headings, a transitional serif and a geometric sans. For reading, interface and data, three neutral or humanist sans families.
Applying the "must" filters. Suppose one of the three body candidates has an uppercase I and lowercase l that are identical at 14px. It scores 0 on ambiguous characters and is out, because the app shows account references. Suppose a second lacks tabular figures; it fails the data job. If only one candidate passes every must, the decision is made before taste enters.
Applying the heading filters. Suppose the geometric sans has only regular and bold; the design needs a semibold for subheads, so it fails the weight check. The transitional serif passes and gives the clear structural contrast with a sans body that the pairing rules ask for.
Recording the result. The project readme lists each family, the source URL, the license name and file, the weights and subsets shipped, the font-display value chosen, and the date checked. Anyone who later asks "can we use this in the app store screenshots?" can answer from the readme.
The point of the example is the order: hard requirements eliminate first, preferences decide among survivors. Starting with preference risks investing mockup time in a font that a single must-check would have ruled out.
Edge cases where the simple advice breaks
- CJK and other large scripts. Chinese, Japanese and Korean fonts contain thousands of glyphs, so whole-file downloads are large. Serving them usually relies on the provider's subsetting by
unicode-rangeor on system fonts. MDN'sfont-synthesispage also notes that synthesizing bold or italic for logographic scripts can "impede the legibility or change the meaning of the text", and shows turning synthesis off. - Dark backgrounds. Light text on dark backgrounds can look heavier than the same weight on white. Some teams step body weight down slightly in dark mode; a variable font makes that a one-line change. Test both themes before deciding.
- System font stacks. Using the operating system's UI font costs no download and matches native apps. The trade-off is that the site looks different on each platform and carries less brand voice. It is a strong choice for internal tools and dashboards.
- Logos. A logo is artwork; once outlined it no longer needs the font file. The license question is whether the font's terms allow logo use, not whether the website can load it.
- Emails. Web font support differs between email clients, so the fallback stack is what a share of readers will see. Choose fallbacks you are happy with on their own, then test in the clients your list actually uses.
Six ways font choices go wrong
- Choosing from the alphabet sampler. Specimens set at display size hide small-size problems. Always test with real copy at real sizes.
- Pairing two near-identical families. If a reviewer needs a moment to tell them apart, use one.
- Letting the display face spread. Once a headline face appears in buttons and labels, interface consistency suffers.
- Ignoring the figures. A price table in proportional figures looks ragged and is harder to scan.
- Loading every weight "just in case." Count what the most visited page uses.
- Skipping the license until handoff. Replacing a font after the logo and slides exist is expensive.
Font selection FAQ
How many font families should one site use?
One or two. Use a single family when the product is mostly interface; add a second only for a defined job such as headings. A third needs a specific reason, like code samples, that the first two cannot handle.
Is a serif or a sans-serif better for body text on screens?
Neither category wins by rule. What matters is the specific design: x-height, apertures, spacing and how it renders on your audience's screens. Test candidates from both categories with your real content and decide on the evidence.
Can I make body text smaller than 16px?
WCAG 2.2 has no minimum size requirement, but text must still resize and survive the 1.4.12 spacing overrides. Because browser defaults are usually 16px and users can change that preference, setting body text in rem relative to the default respects readers who need larger text. Smaller sizes suit captions and secondary labels more than paragraphs.
Should headings use a different font from body text?
Only if it adds a clear voice and the two families contrast obviously. A single family can create strong hierarchy through size, weight and spacing, and it halves the loading and licensing work.
What should I record when the font decision is made?
Family names, source URLs, license name and a saved copy of the license file, weights and subsets shipped, the font-display value, and the date the license was checked. That record answers future questions about app use, print or logo work without starting over.
Sources checked for this guide
All sources below were read for this update and checked October 22, 2026:
- MDN, font-size (default size and px accessibility note): https://developer.mozilla.org/en-US/docs/Web/CSS/font-size (checked October 22, 2026)
- MDN, font-variant-numeric: https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant-numeric (checked October 22, 2026)
- MDN, font-synthesis: https://developer.mozilla.org/en-US/docs/Web/CSS/font-synthesis (checked October 22, 2026)
- MDN, font-display: https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display (checked October 22, 2026)
- MDN, rel=preload: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/preload (checked October 22, 2026)
- W3C, WOFF File Format 2.0 Recommendation: https://www.w3.org/TR/WOFF2/ (checked October 22, 2026)
- WCAG 2.2 Understanding document for 1.4.12, Text Spacing (W3C WAI): https://www.w3.org/WAI/WCAG22/Understanding/text-spacing.html (checked October 22, 2026)
- Google Fonts CSS API v2 documentation: https://developers.google.com/fonts/docs/css2 (checked October 22, 2026)
- google/fonts repository README (license mix): https://github.com/google/fonts (checked October 22, 2026)
- OFL 1.1 license wording, published by SIL: https://openfontlicense.org/open-font-license-official-text/ (checked October 22, 2026)
- OFL frequently asked questions (subsetting, web use, logos): https://openfontlicense.org/ofl-faq/ (checked October 22, 2026)
Where this leads
Once the families are chosen, the next decision is how big each piece of text should be and how those sizes relate. Our typography scale system guide turns the fonts you picked into a set of heading, body and caption sizes with fluid CSS you can paste into a project.