webroad.online
  1. 1Web
  2. 2HTML
  3. 3CSS
  4. 4JavaScript
  5. 5TypeScript
  6. 6Git
  7. 7Tooling
  8. 8React
  9. 9State management
  10. 10Next.js
  11. 11Forms
  12. 12Data and backend
  13. 13SEO
  14. 14Tailwind CSS
  15. 15Animations
  16. 16Testing
  17. 17Architecture
Tailwind CSS · Lesson 4 of 6

Custom typography

Fonts with next/font, the text scale as tokens and composed styles with @utility.

Updated

What custom typography means

A design usually has 5–8 fixed text styles: a big page title, section headings, body text, small text, labels. Each style = a combination of font + size + line height + weight + letter spacing.

The classic mistake: the combination is rewritten by hand everywhere.

<h1 class="font-display text-[2.75rem] leading-[1.1] font-bold tracking-[-0.02em]">
<!-- ... and the same 5 classes in 30 other places -->

The goal: a single name — text-display, text-h2, text-body — defined once and used everywhere, like a class. You change the design in a single place.

Step 1 — the fonts

You load the font with next/font (self-hosted, no layout "jump") and expose it as a CSS variable:

// app/layout.tsx
import { IBM_Plex_Sans, JetBrains_Mono } from 'next/font/google'

const sans = IBM_Plex_Sans({ variable: '--font-plex-sans', subsets: ['latin', 'latin-ext'], weight: ['400', '500', '600', '700'] })
const mono = JetBrains_Mono({ variable: '--font-jetbrains-mono', subsets: ['latin', 'latin-ext'] })

<html className={`${sans.variable} ${mono.variable}`}>

Add the subsets your languages need: latin-ext for accented letters (é, ñ, ș), cyrillic for Russian.

Then you map them into the Tailwind theme:

@theme inline {
  --font-sans: var(--font-plex-sans);       /* font-sans — the default across the site */
  --font-mono: var(--font-jetbrains-mono);  /* font-mono */
  --font-display: var(--font-plex-sans);    /* font-display — a new class */
}

Step 2 — the size scale as tokens

Every --text-* in @theme becomes a text-* class. You can also attach the line height, letter spacing and weight — they all apply from a single class:

@theme {
  --text-display: 3rem;
  --text-display--line-height: 1.1;
  --text-display--letter-spacing: -0.02em;
  --text-display--font-weight: 700;

  --text-h2: 1.75rem;
  --text-h2--line-height: 1.25;
  --text-h2--font-weight: 600;

  --text-body: 1rem;
  --text-body--line-height: 1.65;

  --text-caption: 0.8125rem;
  --text-caption--line-height: 1.4;
}
<h1 class="text-display">Title</h1>
<h2 class="text-h2">Section</h2>
<p class="text-body text-muted">Paragraph</p>
<span class="text-caption">Label</span>

They work with any variant: text-h2 md:text-display.

If you want to completely replace the default scale (text-sm, text-xl...) with your own: --text-*: initial; at the start of @theme. That way the team can use only the sizes from the design.

Step 3 — composed styles with @utility

When a style has more than a font-size (e.g. a different font, text-transform, a color), you create your own utility class:

@utility text-eyebrow {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent);
}
<p class="text-eyebrow md:text-sm">Lesson 3</p>

@utility is better than a normal CSS class: it goes into the utilities layer, it works with variants (hover:, md:, dark:) and it's detected like any Tailwind class.

Typography options — a comparison

Method When
direct Tailwind classes (text-xl font-semibold) once, a special case
a --text-* token with sub-properties the design's size scale — the default
@utility a composed style with a font / transform / color
a <Heading level={2}> component tag + style + logic (e.g. an id for anchors)
the @tailwindcss/typography plugin (prose) long content coming from markdown / a CMS — like the lessons here

Where you keep them

Everything in a single global stylesheet (app/globals.css or src/app/styles/) — the color tokens, fonts, the text scale, the custom utilities. Components only use them.

Summary

  • next/font → a CSS variable → @theme inline { --font-* }.
  • --text-name + --text-name--line-height / --font-weight / --letter-spacing = a complete style in one class.
  • @utility for composed styles; works with every variant.

Official sources

Exercises

Was this page helpful?

One tap — no account needed.