Type something to search...
What is a font family and what are font weights?

What is a font family and what are font weights?

A font family is a group of related fonts that share the same design, such as Roboto Regular, Roboto Italic, Roboto Bold and Roboto Black. Each member of the family varies in one or more ways, most commonly weight, style (upright or italic) and sometimes width. Font weight is the thickness of the strokes in a font, described on a numeric scale from 100 (thinnest) to 900 (heaviest), where 400 is regular and 700 is bold. In CSS, the font-family property chooses the family and the font-weight property chooses which weight within it to use. If the exact weight you ask for isn't available, the browser picks the closest one using a defined set of rules, or fakes a bold if it has to.

Understanding families and weights matters because they're the main tools you have for creating hierarchy without adding more typefaces, and because every extra weight you load adds to page weight. In this article you'll learn how font families are organised, what each weight number means, how browsers match and synthesise weights, how to declare a family properly with @font-face, and how to choose a sensible set of weights for a real website.

What a Font Family Contains

The terms get used loosely, so it helps to be precise. A typeface is the design, a font is one specific file or style of that design, and a font family is the collection of those styles grouped under one name. If you want the full distinction between the first two, see the difference between a typeface and a font.

A typical family includes some or all of these variations:

  • Weight: How heavy the strokes are, from hairline thin to extra black.
  • Style: Upright (often called roman) or italic, sometimes oblique.
  • Width: Condensed, normal or extended versions, for families that offer them.
  • Optical size: Versions drawn for small text or large display use, in some larger families.

A small family might have only four styles: regular, italic, bold and bold italic. Designers sometimes call this the RIBBI set (Regular, Italic, Bold, Bold Italic), and it's the minimum most word processors and operating systems expect for a family to behave normally. Large professional families can contain dozens of styles, for example nine weights in upright and italic across three widths.

Static Fonts and Variable Fonts

Traditionally, each style in a family is a separate file. If you want regular, medium and bold in upright and italic, that's six files.

A variable font stores a whole range of styles in one file. Instead of fixed weights, it has a weight axis that you can set to any value within its range, such as 100 to 900. Many popular families, including Inter, Roboto Flex, Source Sans 3 and Recursive, are available as variable fonts. The family concept stays the same; only the packaging changes.

The Font Weight Scale

CSS uses a numeric scale for weight. The original scale had nine steps in hundreds, and those steps still have standard names that you'll see in font menus and on Google Fonts:

ValueCommon name
100Thin (Hairline)
200Extra Light (Ultra Light)
300Light
400Regular (Normal)
500Medium
600Semi Bold (Demi Bold)
700Bold
800Extra Bold (Ultra Bold)
900Black (Heavy)

The keywords normal and bold are shortcuts for 400 and 700. Since CSS Fonts Level 4, font-weight accepts any number from 1 to 1000, which is mainly useful with variable fonts:

.lead {
  font-weight: 450; /* only distinct from 400 in a variable font */
}

Weight Names Aren't Consistent Between Families

The numbers are a convention, not a measurement. One designer's Medium can be another's Regular. A 400 in a family designed for screen headlines might look as heavy as a 500 in a family designed for books. Some families also name weights like Book (often around 350 to 400) or Heavy (usually 800 or 900) that don't map neatly onto the scale.

This is why you should always look at weights rather than trust their numbers when switching typefaces. A design that used 600 for subheadings in one family might need 700 in another to achieve the same contrast.

How Browsers Choose a Weight

When you set font-weight: 600 and the family has no 600 style, the browser doesn't give up. It follows the font matching algorithm in the CSS Fonts specification:

  1. Desired weight between 400 and 500: It first checks weights from the desired value up to 500, then weights below the desired value in descending order, then weights above 500 in ascending order.
  2. Desired weight below 400: It checks lighter weights first, in descending order, then heavier ones in ascending order.
  3. Desired weight above 500: It checks heavier weights first, in ascending order, then lighter ones in descending order.

In practice this means:

  • If you ask for 600 and only 400 and 700 exist, you get 700.
  • If you ask for 300 and only 400 and 700 exist, you get 400.
  • If you ask for 500 and only 400 and 700 exist, you get 400.

That last case catches people out. Medium text often looks exactly like regular because the medium file was never loaded.

Relative Weights: bolder and lighter

The keywords bolder and lighter set a weight relative to the inherited value, using a fixed table rather than simply adding or subtracting 100:

Inherited weightbolder giveslighter gives
100 to 349400100
350 to 549700100
550 to 749900400
750 to 899900700
900 and aboveUnchanged700

Browsers style strong and b elements with font-weight: bolder by default. So strong inside 400 text becomes 700, but strong inside a 600 heading becomes 900. If your family doesn't include 900, the browser falls back to the nearest heavier weight available, or synthesises one.

Faux Bold

If no suitable bolder weight exists in the loaded family, the browser can draw a synthetic bold by thickening the outlines of the regular style. Faux bold usually looks smeared, with closed-up counters and uneven spacing. You can turn it off:

html {
  font-synthesis-weight: none;
}

The older shorthand font-synthesis: none also disables synthetic italics and small caps. Disabling synthesis is a good way to spot missing weights during development, because text will stay regular rather than quietly faking it.

Declaring a Font Family With @font-face

When you self-host fonts, you build the family yourself. Give every @font-face rule the same font-family name and describe each file with its own font-weight and font-style:

@font-face {
  font-family: "Source Sans 3";
  src: url("/fonts/source-sans-3-regular.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-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-semibold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Source Sans 3";
  src: url("/fonts/source-sans-3-bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

body {
  font-family: "Source Sans 3", system-ui, sans-serif;
  font-weight: 400;
}

h2,
h3 {
  font-weight: 600;
}

The browser now treats these four files as one family and downloads each only when text on the page actually uses that weight and style.

A common mistake is to give each weight its own family name, such as "Source Sans Bold", and set font-weight: 400 on all of them. It works visually, but strong, headings and any font-weight declarations stop mapping to the right file, and the browser may apply faux bold on top of an already bold font.

Declaring a Variable Font

For a variable font, a single rule covers the whole weight range. Give font-weight two values to describe the range:

@font-face {
  font-family: "Inter";
  src: url("/fonts/inter-variable.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

body {
  font-family: "Inter", system-ui, sans-serif;
  font-weight: 400;
}

.subtle {
  font-weight: 350;
}

h1 {
  font-weight: 750;
}

Any value between 100 and 900 now renders a true, designed weight with no synthesis.

Loading Weights From Google Fonts

Google Fonts uses the same idea in its URL. Request specific static weights with a semicolon-separated list:

<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=Lora:ital,wght@0,400;0,700;1,400&display=swap"
  rel="stylesheet">

That loads Lora regular, bold and italic. For a variable family, request a range with two dots:

<link
  href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap"
  rel="stylesheet">

Choosing Which Weights to Use

Most websites need far fewer weights than a family offers. Each static weight is typically somewhere around 15 to 50 KB as WOFF2 for a Latin subset, and they add up quickly.

A practical starting point for many sites is:

  • Regular (400): For body text.
  • Italic (400 italic): For emphasis and citations in body text.
  • Semi Bold or Bold (600 or 700): For headings, buttons and strong.

That's three files, and it covers almost everything. Add a fourth weight only when you have a clear job for it, such as a Medium for navigation labels or a Light for very large display headings.

Weight Contrast Creates Hierarchy

Hierarchy comes from the difference between weights, not from the weights themselves. Regular next to Medium is barely noticeable at body size. Regular next to Bold is obvious. A useful rule is to keep at least a 200-step gap between body text and the weight you use for emphasis:

:root {
  --weight-body: 400;
  --weight-strong: 700;
  --weight-heading: 650; /* variable font */
}

body {
  font-weight: var(--weight-body);
}

strong {
  font-weight: var(--weight-strong);
}

h1,
h2,
h3 {
  font-weight: var(--weight-heading);
}

Thin Weights and Readability

Thin and Extra Light weights look elegant in large headlines but quickly become hard to read at small sizes, especially on low-resolution screens or in light grey. Strokes can drop below a pixel wide and render faintly. Keep weights below 300 for text that's large, such as 32px and up, and make sure it still meets contrast requirements.

Heavy weights have the opposite problem: their counters fill in at small sizes, so letters like e and a start to blob. Black weights are best kept for short, large text.

Checking Which Weight Is Actually Rendering

Because of fallback and synthesis, the weight you set isn't always the weight you get. In Chrome or Edge DevTools, select an element, open the Computed tab and scroll to Rendered Fonts, which lists the actual font file used. Firefox shows similar information in the Fonts panel of the Inspector.

You can also check from the console which faces have loaded:

await document.fonts.ready;

for (const face of document.fonts) {
  if (face.status === "loaded") {
    console.log(face.family, face.weight, face.style);
  }
}
"Source Sans 3" 400 normal
"Source Sans 3" 600 normal
"Source Sans 3" 400 italic

If the bold face isn't in that list but your page has bold text, the browser is either using a different weight or faking it.


FAQ: Font Families and Font Weights

In everyday use they mean almost the same thing. A typeface is the design, and the font family is the set of fonts that make up that design in different weights and styles. In CSS, the font-family property refers to that whole group by name.

Normal body text is weight 400, often called Regular. The CSS keyword normal is the same as 400, and bold is the same as 700.

Most likely the 500 weight isn't loaded. When 500 is requested and missing, browsers look for 400 first, so you get regular text. Load the Medium file or use a variable font with a weight range that includes 500.

Two or three is enough for most sites, usually regular, italic and one bold weight. Add more only if each one has a distinct, repeated job in your design. A variable font can be more efficient if you need four or more styles.

Faux bold is a synthetic bold the browser creates by thickening a lighter font when no bold file is available. Avoid it by loading the bold weight you use, and set font-synthesis-weight to none during development to spot missing weights.

CSS accepts any number from 1 to 1000. With static fonts, the browser rounds to the nearest available weight using its matching rules. Only variable fonts can render in-between values such as 450 as a distinct weight.


Conclusion

A font family is the full set of styles that belong to one typeface, and font weight is the scale, from 100 to 900, that describes how heavy each style is. CSS lets you choose the family with font-family and the weight with font-weight, and when a weight is missing the browser follows predictable rules to find the nearest one or fakes it.

For a real website, declare your family properly so every weight maps to the right file, load only the weights you actually use, and build hierarchy from clear contrast between them. Check rendered fonts in DevTools when something looks off, and turn off weight synthesis if you'd rather see a missing file than a smeared imitation.

Share :

Related Posts

How to make typography accessible?

How to make typography accessible?

You make typography accessible by choosing clear typefaces, setting text in relative units so it scales with user preferences, giving it enough colou

Dive Deeper
What are the parts of a letterform?

What are the parts of a letterform?

A letterform is the shape of a single letter, and typographers break it down into named parts. The main ones are the stem (the main vertical

Dive Deeper
What are ascenders, descenders and the baseline?

What are ascenders, descenders and the baseline?

The baseline is the invisible line that letters sit on. Ascenders are the parts of lowercase letters that rise above the x-height, such as th

Dive Deeper