CSS Fonts and Text: Family, Size, Weight, and Line Height

Published Updated

CSS font properties choose the typeface, size, weight, and line height used to draw text. Text properties adjust spacing, alignment, decoration, and wrapping after the font has been selected.

.article {
  font-family: system-ui, sans-serif;
  font-size: 1rem;
  line-height: 1.6;
  color: #1f2937;
}

.article h2 {
  font-size: 1.75rem;
  font-weight: 700;
  text-wrap: balance;
}

Think of a font stack as a queue of typefaces. The browser checks each option in order for the character and style it needs, then uses the generic family at the end when earlier choices are unavailable.

What CSS Font and Text Properties Control

font-family selects typefaces, font-size sets their scale, and font-weight selects thickness. line-height controls the height of each line box. Then letter-spacing, word-spacing, and text-wrap adjust how text occupies the available line.

Most of these properties inherit, so a body rule can establish readable defaults while headings and controls override only the values that need to differ. That keeps the font queue and its basic measurements consistent across the document.

Build a Font-family Stack

A font-family value is a comma-separated priority list. Quote family names that contain spaces, and always finish with a generic family such as sans-serif, serif, or monospace.

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

code {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
}

The browser tries the named web or local font first, then the platform's user-interface font, then any available sans-serif font. Font selection also happens character by character, so later entries can supply glyphs missing from the first family.

A minimal system-font stack can use system-ui, sans-serif. It loads without a font download and suits interface text, but the exact typeface changes with the operating system.

Set Font Size and Line Height

Use rem for most font sizes so text follows the root size. A unitless line-height multiplies each element's own font size, which lets descendants inherit the proportion instead of a fixed measurement.

body {
  font-size: 1rem;
  line-height: 1.6;
}

h1 {
  font-size: 2.25rem;
  line-height: 1.15;
}

With a 16px root size, body text computes to 16px with a 25.6px line box. The heading computes to 36px with a 41.4px line box. The heading uses a tighter multiplier because large type usually needs less vertical space between lines.

Choose a Font Weight

font-weight accepts keywords such as normal and bold or numbers from 1 to 1000. Common static fonts supply selected values such as 400 and 700, while a variable font may support a continuous range.

.summary {
  font-weight: 400;
}

.summary strong {
  font-weight: 700;
}

Request weights that the loaded font actually provides. If the exact value is missing, the browser chooses an available weight and may synthesize a bold face unless font synthesis is disabled. Two or three intentional weights usually create enough hierarchy for a page.

Adjust Letter and Word Spacing

letter-spacing changes the space between characters, while word-spacing changes the space between words. The initial normal value lets the browser and font use their expected spacing.

.eyebrow {
  font-size: 0.875rem;
  letter-spacing: 0.04em;
}

.article {
  letter-spacing: normal;
  word-spacing: normal;
}

A small spacing adjustment can suit a short label, but body text should usually keep the font's normal character and word spacing. Large positive or negative values can separate letters from their word shapes or crowd them together.

Control Text Wrapping

The text-wrap shorthand controls soft line wrapping. wrap is the normal behavior, nowrap keeps text on one line and can cause overflow, and balance redistributes short blocks across a limited number of lines. The pretty value asks the browser to favor improved body-copy wrapping.

.card__title {
  text-wrap: balance;
}

.card__summary {
  text-wrap: pretty;
}

.status-label {
  text-wrap: nowrap;
}

Use balance for headings rather than long paragraphs. Reserve nowrap for compact content that has a clear overflow plan, since an unbroken label can push beyond a narrow container.

Build a Readable Article Card

Begin with inherited body defaults, then change the title and metadata one property at a time.

body {
  font-family: system-ui, sans-serif;
  font-size: 1rem;
  line-height: 1.6;
}

.article-card {
  max-width: 42rem;
}

.article-card__title {
  margin: 0;
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.2;
  text-wrap: balance;
}

.article-card__meta {
  font-size: 0.875rem;
  color: #4b5563;
}

The card inherits the system-font queue and readable body line height. Its title increases size and weight while tightening line height, and the metadata steps down in size without changing the family. A maximum width prevents the text line from stretching across an unrestricted container.

Common Pitfalls & Debugging

A Web Font Briefly Hides Text

Symptom: text stays invisible while a font downloads. Cause: the chosen font-display strategy includes a block period. Fix: choose an appropriate font-display value so a fallback can appear while the file loads.

@font-face {
  font-family: "Article Sans";
  src: url("/fonts/article-sans.woff2") format("woff2");
  font-display: swap;
}

With swap, the browser can show fallback text promptly and replace it when Article Sans becomes available.

The Fallback Font Changes Line Breaks

Symptom: text shifts when the web font replaces its fallback. Cause: the two fonts have different character widths or vertical metrics. Fix: choose a fallback with similar proportions and test headings, controls, and narrow cards during the swap.

A Fixed Line Height Does Not Scale

Symptom: inherited headings look cramped or overlap. Cause: a parent supplied a fixed line height such as 24px. Fix: use a unitless value so each descendant multiplies the line height by its own font size.

A System-font Stack Varies by Platform

Symptom: text wraps differently on Windows, macOS, and mobile devices. Cause: system-ui selects the platform's interface font. Fix: treat platform variation as part of the system-font choice and test the layout with realistic content instead of assuming identical metrics.

Conclusion

Build a dependable font stack, size it with root-relative values, and use unitless line height so text scales with its element. Test fallback metrics anywhere line breaks affect the layout.

Frequently Asked Questions

Does bold render the same weight in every typeface?

No. The bold keyword maps to whichever weight that family supplies for it, and families differ markedly in how heavy their bold is. A stack that falls back to another font can shift noticeably as a result.

What is a good line height for body text?

A unitless value around 1.5 is a useful starting point for body text, then the typeface and line length should guide the final choice. Headings often use a tighter value because their letters are larger and their lines are shorter.

Can letter-spacing use units other than em?

Yes, any CSS length works. em is the common choice because it scales with the element's own font size, so the spacing stays proportional if the text is later set larger or smaller.

Does text-transform change the underlying text?

No. It changes only how the text is drawn, so the source, the copied text, and what a screen reader receives all keep their original case. Use it for presentation, never to store a value in a different case.

Does text-wrap: balance have consistent browser support?

It is a progressive enhancement: a browser that does not recognise the value ignores it and keeps normal wrapping, so nothing breaks where it is unsupported. Keep it to headings and short blocks, since implementations cap it at a small number of lines for performance reasons.

Sources

  1. [1]
    font-family
    (developer.mozilla.org)
  2. [2]
    Fundamental text and font styling
    (developer.mozilla.org)
  3. [3]
    font-weight
    (developer.mozilla.org)
  4. [4]
    line-height
    (developer.mozilla.org)
  5. [5]
    letter-spacing
    (developer.mozilla.org)
  6. [6]
    word-spacing
    (developer.mozilla.org)
  7. [7]
    text-wrap
    (developer.mozilla.org)
  8. [8]
    font-display
    (developer.mozilla.org)