CSS Typography

Schriftgewichte verstehen: 100 bis 900 erklärt

Updated Februar 24, 2026
Die Benennung von Schriftgewichten ist inkonsistent und verwirrend. Hier ist der definitive Leitfaden zur 100-900-Skala, CSS-Zuordnung und Gewichtsbereichen bei Variable Fonts.

Understanding Font Weights: 100 to 900 Explained

Font weight is one of the most expressive typographic tools available in CSS. The spectrum from 100 (ultra-thin) to 900 (ultra-black) creates a visual hierarchy that guides the eye, communicates importance, and establishes brand character.

Yet font weights are widely misunderstood. Developers frequently set font-weight: bold without knowing what weight value that resolves to, load weights that don't actually exist in a given font, or unknowingly rely on the browser's weight synthesis, which produces noticeably inferior results.

This guide covers the full weight system: what the values mean, how named keywords map, how variable fonts change the game, what happens when a weight is missing, and how to load multiple weights efficiently.


The 100-900 Weight Scale

CSS defines font weight on a numeric scale from 1 to 1000, though in practice the values are expressed in multiples of 100 from 100 to 900. These values map to the weight names used in traditional typography and in font file naming conventions.

.weight-100 { font-weight: 100; } /* Thin / Hairline */
.weight-200 { font-weight: 200; } /* Extra Light / Ultra Light */
.weight-300 { font-weight: 300; } /* Light */
.weight-400 { font-weight: 400; } /* Regular / Normal */
.weight-500 { font-weight: 500; } /* Medium */
.weight-600 { font-weight: 600; } /* Semi Bold / Demi Bold */
.weight-700 { font-weight: 700; } /* Bold */
.weight-800 { font-weight: 800; } /* Extra Bold / Ultra Bold */
.weight-900 { font-weight: 900; } /* Black / Heavy */

Each step in the scale is a 100-unit increment. With static fonts, you are limited to these specific values — and only the values for which you have loaded a font file. With variable fonts, any numeric value between 1 and 1000 is valid.

Practical Weight Usage in UI Design

Most interfaces use three to four distinct weights:

:root {
  --weight-light:    300; /* Decorative, large display text */
  --weight-regular:  400; /* Body copy, default */
  --weight-medium:   500; /* UI labels, slightly emphasized text */
  --weight-semibold: 600; /* Sub-headings, strong emphasis */
  --weight-bold:     700; /* Headings, strong CTAs */
}

body { font-weight: var(--weight-regular); }
.label { font-weight: var(--weight-medium); }
h3, h4 { font-weight: var(--weight-semibold); }
h1, h2 { font-weight: var(--weight-bold); }

Using too many weights creates visual noise and increases download size. Loading three or four is usually enough for a complete visual hierarchy.


Named Weights: Thin, Light, Regular, Bold, Black

In addition to numeric values, CSS provides two keyword values: normal and bold.

/* Keyword equivalents */
.normal { font-weight: normal; } /* Equivalent to 400 */
.bold   { font-weight: bold; }   /* Equivalent to 700 */

The keywords lighter and bolder are relative to the inherited weight and follow a lookup table:

/* bolder and lighter follow this table:
   Inherited: 100-300 → bolder = 400
   Inherited: 400-500 → bolder = 700
   Inherited: 600-900 → bolder = 900

   Inherited: 100-500 → lighter = 100
   Inherited: 600-700 → lighter = 400
   Inherited: 800-900 → lighter = 700
*/

.parent { font-weight: 400; }
.child  { font-weight: bolder; } /* Becomes 700 */
.grandchild { font-weight: bolder; } /* Becomes 900 */

Font Family Naming Conventions

Font families use various naming conventions for their weight variants. These don't always map predictably to CSS values. Here is the common mapping:

File/Style Name CSS Value
Thin, Hairline 100
Extra Light, Ultra Light 200
Light 300
Regular, Roman, Normal, Book 400
Medium 500
Semi Bold, Demi Bold 600
Bold 700
Extra Bold, Ultra Bold 800
Black, Heavy, Ultra Black 900

Some families have additional named weights not covered by CSS: "Compressed", "Ultra Compressed", or brand-specific names like "Display". These may map to non-standard numeric weights or to custom variable font axes.

Which Weights Google Fonts Offers

Not all fonts on Google Fonts offer all nine weights. Some highlights:

Inter: 100, 200, 300, 400, 500, 600, 700, 800, 900 (variable font, full range) Roboto: 100, 300, 400, 500, 700, 900 Open Sans: 300, 400, 500, 600, 700, 800 Lato: 100, 300, 400, 700, 900 Montserrat: 100, 200, 300, 400, 500, 600, 700, 800, 900

Always check which weights a font actually offers before designing around a weight. Loading a weight that doesn't exist triggers weight synthesis, which is covered below.


Font Weight in Variable Fonts

Variable fonts fundamentally change how font weight works. Instead of discrete files for specific weights, a single variable font file contains the entire weight spectrum defined by the type designer.

/* Variable font with full weight range */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/Inter-Variable.woff2') format('woff2');
  font-weight: 100 900; /* Range — covers any value in between */
  font-display: swap;
}

/* Now any numeric weight value is valid */
.hero-thin      { font-weight: 100; }
.body-text      { font-weight: 400; }
.ui-medium      { font-weight: 500; }
.half-step      { font-weight: 450; } /* Not possible with static fonts */
.almost-bold    { font-weight: 650; } /* Not possible with static fonts */
.heading        { font-weight: 700; }
.extra-heavy    { font-weight: 850; } /* Not possible with static fonts */

The ability to specify non-multiple-of-100 weight values is one of the most useful capabilities of variable fonts in practice. It enables:

Dark mode weight adjustment: Dark backgrounds make text appear optically heavier. Reducing weight from 400 to 370 on dark backgrounds maintains the same perceived weight:

@media (prefers-color-scheme: dark) {
  body { font-weight: 370; }      /* Slightly lighter than 400 */
  strong { font-weight: 580; }   /* Slightly lighter than 600 */
}

Optical weight matching: Large text at the same font-weight value looks heavier than small text. Reducing weight slightly for display sizes matches the visual weight to the designer's intent:

h1 {
  font-size: 4rem;
  font-weight: 680; /* Feels like 700 but optically correct at large size */
}

h3 {
  font-size: 1.5rem;
  font-weight: 700; /* Standard bold at normal heading size */
}

Smooth hover animations: Animating weight on hover creates a satisfying interactive effect without layout shift (since variable font weight changes don't change metrics significantly in most cases):

.nav-link {
  font-weight: 400;
  transition: font-weight 200ms ease;
}

.nav-link:hover {
  font-weight: 600;
}

Weight Synthesis: What Happens When a Weight Is Missing

When you request a font-weight that the font family doesn't have a file for, the browser synthesizes it. This is automatic and silent — there's no error.

How Synthesis Works

Bold synthesis: When you request font-weight: 700 but only 400 is loaded, the browser applies an algorithmic darkening/thickening of the 400 strokes. The result is almost always visually inferior to a properly designed bold face — it applies a uniform stroke widening that doesn't account for the optical adjustments a type designer makes when drawing a bold weight.

Thin/light synthesis: When you request font-weight: 300 but only 400 is loaded, the browser applies an algorithmic thinning. The results are similarly imperfect.

/* This triggers synthesis if only the regular (400) weight is loaded */
strong {
  font-weight: bold; /* 700 — will be synthesized if 700 isn't loaded */
}

/* To prevent synthesis, load the weight you need */
@font-face {
  font-family: 'Open Sans';
  src: url('/fonts/OpenSans-Bold.woff2') format('woff2');
  font-weight: 700; /* Load the real bold */
  font-display: swap;
}

Detecting Synthesis Problems

You can see synthesis in action by comparing text rendered in a synthesized weight versus a proper weight. The most visible difference is usually: - Synthesized bold: uniform stroke widening, sometimes blurry, poor spacing - Real bold: carefully adjusted strokes, maintained spacing, intentional design

Open browser DevTools and look at the "Fonts" section of the Elements panel. Chrome DevTools shows you exactly which font file is being used for each element, and whether synthesis is being applied.

Disabling Synthesis

You can prevent the browser from synthesizing weights using font-synthesis:

/* Disable all synthesis */
body {
  font-synthesis: none;
}

/* Disable only weight synthesis (allow style synthesis) */
body {
  font-synthesis: style;
}

/* Disable only italic synthesis (allow weight synthesis) */
body {
  font-synthesis: weight;
}

Setting font-synthesis: none means that if a requested weight isn't available, the browser renders the nearest available weight instead of synthesizing. This can result in text that's lighter or heavier than intended, but it prevents the quality degradation of synthesis.

The right approach is to load the weights you need rather than rely on synthesis at all.


Best Practices for Loading Multiple Weights

1. Load Only What You Need

Every weight variant is a separate file download (for static fonts) or increases the size of the variable font file. Audit your design and load only the weights you actually use.

/* Audit your design — do you actually need all these? */
@font-face { font-weight: 100; src: url('thin.woff2'); }      /* Thin headlines? */
@font-face { font-weight: 200; src: url('el.woff2'); }        /* Rarely needed */
@font-face { font-weight: 300; src: url('light.woff2'); }     /* Subheadings? */
@font-face { font-weight: 400; src: url('regular.woff2'); }   /* Body — YES */
@font-face { font-weight: 500; src: url('medium.woff2'); }    /* UI labels? */
@font-face { font-weight: 600; src: url('semibold.woff2'); }  /* Strong labels? */
@font-face { font-weight: 700; src: url('bold.woff2'); }      /* Headings — YES */
@font-face { font-weight: 800; src: url('extrabold.woff2'); } /* Rarely needed */
@font-face { font-weight: 900; src: url('black.woff2'); }     /* Display only */

For most body-copy-focused sites, you need exactly two weights: 400 and 700. For UI-heavy applications, add 500 or 600.

2. Prefer Variable Fonts for Multiple Weights

If you need three or more weights, a variable font is almost always more efficient:

/* Static: 4 files, 4 HTTP requests, ~68 KB total */
@font-face { font-weight: 300; src: url('light.woff2'); }
@font-face { font-weight: 400; src: url('regular.woff2'); }
@font-face { font-weight: 600; src: url('semibold.woff2'); }
@font-face { font-weight: 700; src: url('bold.woff2'); }

/* Variable: 1 file, 1 HTTP request, ~45 KB, full range */
@font-face {
  font-weight: 100 900;
  src: url('variable.woff2') format('woff2');
}

3. Define Weight Variables Once

:root {
  --weight-body:     400;
  --weight-ui:       500;
  --weight-emphasis: 600;
  --weight-heading:  700;
  --weight-display:  800;
}

body          { font-weight: var(--weight-body); }
.label        { font-weight: var(--weight-ui); }
strong, b     { font-weight: var(--weight-emphasis); }
h3, h4, h5   { font-weight: var(--weight-heading); }
h1, h2       { font-weight: var(--weight-display); }

This makes weight auditing simple: look at your :root variables to see every weight in use, and ensure each maps to a loaded font face.

4. Preload the Most Critical Weight

For your primary body font, preload the regular (400) weight to eliminate render-blocking:

<!-- Preload the most critical weight — regular body text -->
<link
  rel="preload"
  href="/fonts/inter-400.woff2"
  as="font"
  type="font/woff2"
  crossorigin
>

Non-critical weights (bold for headings, etc.) can load on demand without preloading.

5. Use font-display: swap

Always include font-display: swap so users see text in the fallback font immediately rather than waiting for the web font:

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-400.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-700.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
}

The weight scale is one of the most powerful tools in your typographic toolkit. Used well — with deliberate choices about which weights to load, careful matching of weights to their intended use, and an understanding of how variable fonts expand the possibilities — it creates a clear, legible visual hierarchy with minimal overhead.

Typography Terms

Try These Tools

Fonts Mentioned

Roboto Sans Serif #1

Designed by Christian Robertson for Google's Material Design ecosystem, this neo-grotesque sans-serif is the most widely used typeface on the web and Android. Its dual-nature design balances mechanical precision with natural reading rhythm, making it equally at home in UI labels and long-form text. The variable font supports width and weight axes alongside Cyrillic, Greek, and extended Latin scripts.

The quick brown fox jumps over the lazy dog
Inter Sans Serif #5

Rasmus Andersson spent years refining this neo-grotesque specifically for computer screens, optimizing letter spacing, x-height, and stroke contrast for high readability at small sizes on digital displays. An optical size axis (opsz) lets the font automatically adjust its design for captions versus headlines, while the weight axis covers the full range from thin to black. It has become the de facto choice for dashboards, documentation sites, and developer tools worldwide.

The quick brown fox jumps over the lazy dog
Montserrat Sans Serif #6

Inspired by the geometric signage and storefronts of the Montserrat neighborhood in Buenos Aires, Julieta Ulanovsky created this typeface to capture the spirit of early 20th-century urban lettering. Clean circular forms and strong geometric proportions give it an assertive presence ideal for headlines, branding, and landing pages. The variable weight axis spans a wide range, and Cyrillic and Vietnamese scripts are included.

The quick brown fox jumps over the lazy dog

Related Articles