
What Is a Type Family, and How Many Weights Do You Need?
- Sajjad
- Typography
- 01 Oct, 2026
You open the Google Fonts page for Inter, click "Get font", and the embed code asks for every weight from 100 to 900, in both upright and italic. Your designer's Figma file uses Regular, Medium, and Bold. Your production site is now downloading eighteen styles to render three, and the Lighthouse report is flagging render-blocking requests. This is one of the most common font problems on the web, and it comes from not knowing what a type family actually contains or how much of it you need.
This article explains what a type family is, how weights, widths, and styles are organized, how they map onto CSS properties, and how to decide on the smallest set of styles that still gives your design enough range. It also covers the trade-off between static files and a single variable font, and how to load only what you use in plain CSS, Next.js, and Tailwind CSS v4.
What Is a Type Family?
A type family (or font family) is a group of related fonts designed to work together under one name. Every member shares the same skeleton, proportions, and design details, but varies along one or more axes: weight, width, slope, or optical size.
The terms get muddled, so here is the hierarchy:
| Term | What it means | Example |
|---|---|---|
| Superfamily | Several families designed to share a common structure | IBM Plex (Sans, Serif, Mono) |
| Family | One named design with all its variations | IBM Plex Sans |
| Style | A single member of the family | IBM Plex Sans SemiBold Italic |
| Font file | The file that delivers one style (or many, if variable) | IBMPlexSans-SemiBoldItalic.woff2 |
If the difference between a typeface and a font still feels fuzzy, the post on typeface vs. font covers it. For this article, what matters is that a family is a toolkit, and you rarely need the entire toolkit.
The Axes of a Type Family
Weight
Weight is the thickness of the strokes. OpenType and CSS use a numeric scale from 1 to 1000, with conventional names at each hundred:
| Value | Common name |
|---|---|
| 100 | Thin (Hairline) |
| 200 | Extra Light (Ultra Light) |
| 300 | Light |
| 400 | Regular (Normal, Book) |
| 500 | Medium |
| 600 | Semi Bold (Demi Bold) |
| 700 | Bold |
| 800 | Extra Bold (Ultra Bold) |
| 900 | Black (Heavy) |
Names are not standardized across foundries. One family's "Book" is 400, another's is 350, and some families ship a "Heavy" that sits at 850. Always check the numeric usWeightClass value or the wght axis range rather than trusting the name.
Width
Width (sometimes called stretch) describes how condensed or extended the letters are. CSS expresses it with font-stretch or the wdth axis, as a percentage from 50% (ultra-condensed) to 200% (ultra-expanded), with 100% as normal. Families like Roboto Flex, Archivo, and Barlow offer condensed widths that are useful for tight UI spaces, data tables, and headlines.
Style: Italic and Oblique
A true italic is a separately drawn design with different letter shapes: a single-storey "a," a curved "f" that may descend, and often narrower proportions. An oblique is the upright design slanted mechanically. Many sans-serif families ship obliques and call them italics, which is fine, but it is worth knowing which one you have, because a browser can fake an oblique but cannot fake a true italic convincingly.
Optical Size
Some families vary their design for the size they will be read at: wider spacing and sturdier details for small text, finer contrast for large display text. This is covered in the post on optical sizing.
How Families Map to CSS
CSS groups font files into a family through @font-face rules that share the same font-family name. Each rule describes which weight and style its file covers. The browser then picks the right file when you set font-weight or font-style.
@font-face {
font-family: "Source Sans 3";
src: url("/fonts/source-sans-3-400.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Source Sans 3";
src: url("/fonts/source-sans-3-400-italic.woff2") format("woff2");
font-weight: 400;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "Source Sans 3";
src: url("/fonts/source-sans-3-700.woff2") format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
body {
font-family: "Source Sans 3", system-ui, sans-serif;
}
Browsers only download a file when text on the page actually needs it. If nothing on a page is italic, the italic file is never fetched. That is useful, but it doesn't fix the problem of declaring and preloading styles you never use, and it means the first italic word on a page can trigger a late download and a visible swap.
What Happens When a Weight Is Missing
If you ask for font-weight: 600 and only 400 and 700 exist, the browser follows the CSS font matching algorithm: for weights above 500, it looks for the nearest heavier weight first, so you get 700. For weights below 400, it looks lighter first. A 400 request tries 500 before anything lighter, and a 500 request tries 400 first.
If no bold file exists at all, the browser applies synthetic bold by smearing the outline, and if no italic exists, it applies synthetic oblique by slanting the regular. Both look noticeably worse than the real thing. You can switch synthesis off so missing styles fail visibly in development:
html {
font-synthesis: none;
}
Or more selectively, font-synthesis-weight: none and font-synthesis-style: none.
How Many Weights Do You Actually Need?
For most websites, the honest answer is two to four styles. Here is how that breaks down by project type:
| Site type | Typical styles | Count |
|---|---|---|
| Marketing landing page | Regular 400, Bold 700 | 2 |
| Blog or documentation | Regular 400, Italic 400, Bold 700 | 3 |
| Content site with rich headings | Regular 400, Italic 400, Semi Bold 600, Bold 700 | 4 |
| SaaS dashboard / app UI | Regular 400, Medium 500, Semi Bold 600 | 3 |
| Editorial with display headlines | Body family: 400, 400 italic, 700; display family: 1–2 weights | 4–5 |
The logic behind those numbers:
- Regular (400) is mandatory. It sets all body text, which is most of the words on the page.
- One emphasis weight is almost always needed. Bold 700 covers
strong, headings, and buttons. In interfaces, Medium 500 or Semi Bold 600 often works better than 700 because it adds emphasis without looking heavy at small sizes. - Italic is needed if your content has emphasis, citations, or book titles. Long-form content almost always does. Pure UI often doesn't.
- A third weight is a hierarchy tool, not a default. Add Semi Bold or Light only when the design uses it in more than one place and the difference is visible.
The Visible-Difference Test
Adjacent weights like 400 and 500 can look almost identical at 16px, especially on low-density screens. If a design uses both, set them side by side at the actual size and ask whether a user would notice. If they wouldn't, collapse them into one. A useful rule of thumb is to keep at least 200 units between weights that need to read as different, such as 400 and 600, or 500 and 700. The post on using font weight to guide attention goes deeper on weight as a hierarchy tool.
Avoid Thin Weights for Body Text
Weights below 300 thin out quickly on screens, and thin strokes reduce effective contrast even when the color passes WCAG 1.4.3's 4.5:1 ratio. Reserve 100–300 for large display text, if you use them at all.
Static Fonts vs. a Variable Font
A variable font packs an entire range of weights (and sometimes widths and other axes) into one file. Instead of choosing styles, you choose a range.
The size maths decides which approach wins:
| Approach | Files | Approx. size (Latin subset) |
|---|---|---|
| 2 static weights | 2 | ~40–50 KB |
| 4 static styles | 4 | ~80–100 KB |
1 variable file, wght 100–900 | 1 | ~45–70 KB |
| Variable upright + variable italic | 2 | ~90–140 KB |
These are ballpark figures for a typical sans like Inter or Source Sans 3 after Latin subsetting. The pattern holds generally: if you need two weights, static files are usually smaller. Once you need three or more, a variable font tends to break even or win, and it gives you every intermediate weight for free. A full comparison lives in variable fonts vs. static fonts.
Declaring a variable font covers the whole range with one rule:
@font-face {
font-family: "Inter";
src: url("/fonts/inter-var.woff2") format("woff2-variations");
src: url("/fonts/inter-var.woff2") format("woff2") tech(variations);
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
The two src lines are a progressive pattern: older browsers understand the first, and browsers that understand tech() override it with the second.
Loading Only the Styles You Use
Google Fonts CSS API
The Google Fonts CSS2 API lets you request exactly the styles you need. The ital,wght axis tuple lists each style as italic-flag,weight:
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Source+Sans+3:ital,wght@0,400;0,700;1,400&display=swap"
rel="stylesheet"
/>
That loads Regular, Bold, and Italic, nothing else. For a variable range, use wght@400..700.
Next.js with next/font
In the App Router, next/font/google self-hosts the files at build time. For variable families, omit weight to get the full variable axis, or list static weights explicitly:
// app/fonts.ts
import { Source_Sans_3 } from "next/font/google";
export const sourceSans = Source_Sans_3({
subsets: ["latin"],
weight: ["400", "700"],
style: ["normal", "italic"],
display: "swap",
variable: "--font-source-sans",
});
// app/layout.tsx
import { sourceSans } from "./fonts";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className={sourceSans.variable}>
<body>{children}</body>
</html>
);
}
Each weight and style combination you list becomes a file, so keep these arrays short.
Tailwind CSS v4
In Tailwind v4, register the family as a theme variable and restrict the weight utilities you expose so the team can't reach for styles that aren't loaded:
@import "tailwindcss";
@theme inline {
--font-sans: var(--font-source-sans), system-ui, sans-serif;
--font-weight-*: initial;
--font-weight-normal: 400;
--font-weight-bold: 700;
}
Using @theme inline makes the utility reference the next/font variable directly instead of resolving it at the root. Resetting the --font-weight-* namespace removes font-thin, font-medium, and the others, leaving only font-normal and font-bold. It's a cheap guardrail that keeps code and loaded files in sync.
Auditing an Existing Site
If you inherit a site with too many weights, work through it in this order:
- List what's loaded. In Chrome DevTools, open the Network panel, filter by "Font", and reload. Every row is a style someone pays for.
- List what's used. Search the CSS for
font-weightandfont-stylevalues, including utility classes likefont-semibold. The Rendered Fonts section in the Elements panel's Computed tab shows which font file rendered a selected element. - Merge near-duplicates. Map 500 to 400 or 600, and 800 to 700, where the visual difference doesn't matter.
- Remove unused declarations and any
preloadlinks for them. - Turn on
font-synthesis: nonein development to catch anything you removed by mistake.
Type Family FAQ
In everyday web work, yes. Type family is the traditional design term and font family is the term CSS uses in the font-family property. Both refer to a named design and all of its weights, widths, and styles.
Medium usually sits at weight 500 and Semi Bold at 600. Medium is a subtle step up from Regular that works well for UI labels and buttons, while Semi Bold reads clearly as emphasis and is a common choice for subheadings.
Yes, if you want it to look right. Without a bold file, the browser synthesizes bold by thickening the regular outlines, which produces blurry, uneven strokes. Loading one real bold file is a small cost for a noticeably better result.
No. Many display families and some sans-serifs ship without an italic. If your content needs italics, check before choosing the family, because synthetic oblique is a poor substitute and a mismatched italic from another family usually looks wrong.
Not always. If you only need two weights, two static files are often smaller than one variable file. Variable fonts pay off when you need three or more weights, intermediate values, or animation along an axis.
A superfamily is a collection of separate families designed to share a common structure, such as a sans, a serif, and a mono. IBM Plex, Source (Sans, Serif, Code), and Noto are well-known examples. They make pairing easy because the proportions already match.
Conclusion
A type family is a coordinated set of styles that vary along weight, width, slope, and sometimes optical size. Knowing the structure helps you read a font specimen properly, map styles to CSS accurately, and understand what the browser does when a style is missing.
For most sites, two to four styles are enough: Regular for body text, one emphasis weight, an italic for content-heavy pages, and possibly one more weight for hierarchy. Pick weights that are visibly different at the sizes you use, decide between static files and a variable font based on how many styles you need, and load only those styles through the Google Fonts API, next/font, or your own @font-face rules.
Here are some useful references for going deeper on type families and font weights:
- MDN Web Docs: font-weight — the numeric scale, fallback weight matching, and variable font support.
- MDN Web Docs: font-synthesis — how to control synthetic bold and oblique.
- W3C: CSS Fonts Module Level 4 — the specification for
@font-face, font matching, and variable font descriptors. - Google Fonts Knowledge: Google Fonts Knowledge — introductory guides to weights, styles, and variable fonts.
- Next.js Docs: Font Optimization — configuring
next/fontweights, styles, and subsets.


