CSS typography for interfaces

Sep 18, 2026 · Type · 3 min read

A heading breaks over two lines and leaves one word sitting alone underneath. Nobody files a bug about it, but everyone sees it, and it makes a careful design look unfinished. That lonely word is the smallest sign of a bigger truth: interface text lives a harder life than print.

It gets translated, filled with user data, squeezed into narrow columns and updated every second. The rules below are the small CSS settings that keep it looking deliberate under all of that.

No word alone on the last line

A heading that breaks with one word stranded on its second line looks accidental. Two properties fix most of it:

wrap

Interfaces that feel obvious the first time

Good motion explains itself. Nobody should have to notice it.

Not recommended
balance + pretty

Interfaces that feel obvious the first time

Good motion explains itself. Nobody should have to notice it.

Recommended
Width220px
h1, h2, h3 {
  text-wrap: balance; /* evens out line lengths */
}
p.description, figcaption {
  text-wrap: pretty; /* avoids a single orphaned word */
}

balance is meant for short text; browsers only balance a handful of lines and skip longer blocks. pretty costs a little more to compute, so leave long articles on the default and use it for descriptions, captions and card copy.

Numbers that change need fixed widths

In most fonts, a 1 is narrower than an 8. When a timer, counter or price ticks, the digits change width and the whole number shivers from side to side. Tabular figures give every digit the same width.

1000
Not recommended
1000
Recommended
.timer, .price, td.numeric {
  font-variant-numeric: tabular-nums;
}

Use it in tables too, so columns of numbers line up by place value. Leave it off in running prose, where proportional figures read more naturally.

Keep lines readable

Past about 75 characters a line, the eye loses its place when it jumps back to the start of the next one. Under about 45, reading gets choppy. Cap body text with the ch unit, which scales with the font.

Not recommended

Lines that run the full width of a wide container, well past 75 characters.

Recommended

The same text with its measure capped, so each line stays between 60 and 75 characters.

.prose {
  max-width: 65ch;
}

Keep text inside its box

User content will contain a 60-character URL, an email address or an ID with no spaces. Without help it overflows its container and breaks the layout. Labels and badges have the opposite problem: they should never wrap at all.

break-word
Not recommended

Your token tk_7Qm4xRz9vL2pWd8nKcYb3sH is live.

Recommended

Your token tk_7Qm4xRz9vL2pWd8nKcYb3sH is live.

nowrap
Not recommended
Updated 3 min ago
Recommended
Updated 3 min ago
.user-content {
  overflow-wrap: break-word; /* breaks long tokens only when needed */
}
.badge {
  white-space: nowrap; /* never splits in half */
}

When you do truncate with an ellipsis, give the full text a way out: a tooltip, a title attribute at minimum, or a way to expand. Truncation without escape hides information.

Interfaces that feel obvious the first time, for everyone who opens them.

Small settings worth setting once

  • Links: text-underline-position: from-font and text-decoration-skip-ink: auto, so underlines sit where the type designer intended and skip around descenders.
  • Font files: serve .woff2 only. It is compressed and supported everywhere that matters; .ttf and .otf are desktop formats and much heavier.
  • Smoothing: -webkit-font-smoothing: antialiased on the root gives lighter, crisper text on macOS, which suits most interface type.
  • Case: write copy in sentence case and apply uppercase with text-transform when a design needs it. The source stays readable and screen readers do not spell it out.

Checklist

  • Headings use text-wrap: balance; short descriptions use pretty.
  • Changing numbers use tabular-nums.
  • Reading text is capped around 65ch.
  • User content breaks long words; badges never wrap.
  • Every truncation has a way to see the full text.
  • Fonts are served as woff2.

This piece expands on the Type rules in my cheat sheet, where every rule has a live example.

Keep readingStructuring color tokensPrimitives versus semantic tokens, naming by role, trimming the gray ramp and building a dark theme that is not just inverted.