Designing a Typography System That Scales
Typography is one of the most important parts of any website, yet it is often treated as a purely visual detail.
Good typography improves readability, guides the user through the content, and creates visual hierarchy. A well designed typography system also scales naturally across different screen sizes.
Instead of setting random font sizes throughout the interface, it helps to think of typography as a small system of rules.
Fluid typography with clamp()
Modern CSS provides a very useful function for responsive typography called clamp().
It allows font sizes to scale smoothly between a minimum and maximum value depending on the viewport width.
Example:
h1 {
font-size: clamp(1.75rem, 1.25rem + 2vw, 2.5rem);
}
p {
font-size: clamp(1rem, 0.95rem + 0.2vw, 1.125rem);
} This approach avoids the need for multiple media queries while keeping text readable across devices.
The browser automatically adjusts the font size within the defined range.
Readable line length
Another important typography principle is line length.
Lines that are too long become tiring to read. Lines that are too short break the reading rhythm.
A commonly recommended range is around 60 to 70 characters per line.
CSS makes this easy to control using the ch unit.
article {
max-width: 65ch;
} This ensures that paragraphs stay readable even on very wide screens.
Combined with centered containers, it creates a comfortable reading experience for long form content.
Important nuance
chis not exactly the width of every character. It is only based on the width of 0, which is a monospaced reference. Some letters are wider or narrower depending on the font. But for text layout it is still a very good approximation, which is why it is widely used.
Spacing rhythm
Typography is not only about font size. Spacing between elements plays a huge role in readability.
A consistent vertical rhythm helps readers scan and understand content more easily.
For example headings should create clear separation from surrounding text.
h1 {
margin-bottom: 1rem;
}
h2 {
margin-top: 2rem;
margin-bottom: 0.75rem;
}
p {
margin-bottom: 1rem;
} The exact values are less important than the consistency.
Once a spacing rhythm is established it should remain predictable throughout the interface.
Tailwind design tokens
> Tailwind v3 - Config first?
When using Tailwind or other utility frameworks, typography systems are often implemented through design tokens.
Instead of defining values directly in every component, shared tokens describe the system.
In earlier versions of Tailwind, this was typically done in the JavaScript configuration file.
theme: {
extend: {
fontSize: {
body: "clamp(1rem, 0.95rem + 0.2vw, 1.125rem)",
h1: "clamp(1.75rem, 1.25rem + 2vw, 2.5rem)",
h2: "clamp(1.4rem, 1.1rem + 1vw, 1.8rem)"
}
}
} This approach worked well. It centralized typography values and made them reusable through utility classes.
But it also meant that your design system lived partly in JavaScript and partly in CSS.
> Tailwins v4 - CSS-first!
In Tailwind CSS v4, this has shifted to a more CSS-first approach.
Instead of defining tokens in a config file, you define them directly in your CSS using the @theme directive.
@import "tailwindcss";
@theme {
--text-body: clamp(1rem, 0.95rem + 0.2vw, 1.125rem);
--text-body--line-height: 1.6;
--text-h1: clamp(1.75rem, 1.25rem + 2vw, 2.5rem);
--text-h1--line-height: 1.1;
--text-h2: clamp(1.4rem, 1.1rem + 1vw, 1.8rem);
--text-h2--line-height: 1.2;
} These tokens automatically become available as utility classes.
<h1 class="text-h1">Heading</h1>
<h2 class="text-h2">Subheading</h2>
<p class="text-body">Content</p> This is an important shift to understand. It’s not just syntax, it is where the system lives. With this your typography is now defined in the same place as the rest of your styles, using native CSS primitives.
> Reusing tokens outside utilities
Another advantage of this approach is that the same tokens are exposed as CSS variables.
That makes it easy to reuse them in places where utility classes are not practical, for example when styling rich text or Markdown content.
.article-content p {
font-size: var(--text-body);
line-height: var(--text-body--line-height);
}
.article-content h1 {
font-size: var(--text-h1);
line-height: var(--text-h1--line-height);
} This keeps everything aligned. Whether you are using utilities or writing custom CSS, the same typography rules apply.
> Keep the system simple
The goal is not to define as many tokens as possible. It is to define just enough. A small, consistent set of tokens is easier to reason about, easier to maintain, and much harder to break over time.
Just like with layout, a simple system usually scales better than a complex one.
Summary
Good typography is not about decoration. It is about structure and clarity.
When font sizes, spacing, and line length follow clear rules, the content becomes easier to read and easier to maintain.
In our next article we are diving into another important Accessibility topic. Building Navigation that works for everyone.
This is part of our “Building a Modern Website” series where we share how we approach software development, architecture and frontend engineering. If you’re building something similar, we also work as software development consultants. Check our Services for more details.
