Typography and color

Use font families

Choose built-in font stacks and add a project font with Tailwind CSS v4 theme variables when the design genuinely needs one.

Tailwind ships three font family utilities: font-sans, font-serif, and font-mono.

Each one sets a whole font stack, a list of families the browser tries in order. font-sans gives you ui-sans-serif, system-ui, sans-serif and a few more fallbacks. If the first family isn’t installed, the browser moves to the next. That’s why a stack is safer than naming one font.

Add a project font in v4

In Tailwind v4 you add a font in CSS, with a theme variable:

@theme {
  --font-display: "Geist", ui-sans-serif, system-ui, sans-serif;
}

Now font-display exists as a utility:

<h1 class="font-display text-4xl font-semibold">Products</h1>

The variable becomes a utility because its name starts with --font-, which is Tailwind’s namespace for font families. Any --font-* variable in @theme produces a matching font-* class. If you only want a CSS variable, without a new utility, put it in :root instead.

The stack doesn’t load the font

Declaring the family tells the browser what to look for. It doesn’t download anything. You still need an @font-face rule, your framework’s font integration, or a hosted stylesheet.

Match the weights too. If you load only the regular file and then write font-bold, the browser synthesizes a bold version. It looks blurry compared to the real one. Load a file for every weight and style you use.

Loading and layout shift

While a web font downloads, the browser shows a fallback. If the fallback has very different metrics, the text reflows when the real font arrives. Keep the file count small, cache the files well, and choose a font-display strategy in your @font-face that keeps text readable during the load. Think about privacy before depending on a third-party font host, too.

Fonts by role

A display face for headings and a system stack for body text is a common and reasonable split. Monospace says “this is code” or “these numbers line up”. It’s not automatically more readable, so don’t use it for every label.

Try this: add one local display font with a system fallback. Block the font request in the DevTools Network panel, compare the layout shift and readability with the fallback, and confirm every weight you request has a real font file.

Lesson completed