Every design system starts with a type scale that looks great in Figma. Six or seven sizes, a clean ratio between them, everything lining up on an 8px grid. Then it ships, and by month three someone has added font-size: 15px !important to a component because nothing in the scale quite fit, and the system starts eroding from the inside.
The failure isn't usually the ratio. It's that most type scales are designed for one viewport and then stretched, shrunk, or overridden to survive everywhere else. A scale that actually holds up is built with the full range of devices in mind from the start, not patched after the fact.
Start with a ratio, not a list of pixel values
A type scale is a set of sizes related by a consistent ratio, not an arbitrary list. Common ratios run from 1.125 (subtle, editorial) to 1.5 (dramatic, marketing-heavy). Pick one ratio and generate the full scale from it rather than hand-picking sizes that feel right individually, which is how you end up with a 22px and a 24px that nobody can explain the difference between.
The modular scale concept, borrowed from print typography, is the right mental model here: a base size multiplied and divided by the ratio to generate steps above and below it. Six steps is usually enough for a marketing site. A dense application UI might need eight or nine.

Photo by M.Emin BİLİR on Pexels
Use relative units for the base, not fixed pixels
Setting your base font size in rem rather than px means it respects the user's browser zoom and OS-level text size preference, which matters more than most teams treat it. A user who's bumped their default font size to 20px in browser settings because of low vision should get a proportionally larger scale across your entire site, not a site that ignores the setting because every size was hardcoded in pixels.
This single decision, rem for the base and everything derived from it, is the difference between a typography system that respects accessibility settings and one that quietly breaks them. MDN's documentation on CSS length units covers the distinction between absolute and relative units in more depth if you want the full technical picture.
Fluid scaling beats breakpoint-jumping
The old approach to responsive type was a handful of media-query breakpoints, each resetting the base size. It works, but it means text jumps in visible steps as the viewport crosses a breakpoint, and there's always an awkward width where the size feels wrong in either direction.
clamp() lets you define a minimum size, a fluid size tied to viewport width, and a maximum size in one declaration, so text scales continuously between breakpoints instead of jumping. A heading might be clamp(1.75rem, 4vw + 1rem, 3rem), meaning it never drops below 1.75rem on a small phone, never exceeds 3rem on a huge display, and scales smoothly in between. It's a meaningfully better experience for the exact width ranges that used to look worst, the awkward middle ground between your breakpoints.
Line height and measure matter as much as size
A type scale that only defines font sizes and ignores line height and line length (measure) is half a system. Line height needs to loosen as text gets smaller and can tighten slightly as it gets larger, because the eye tracks short lines of small text differently than long headline text. A common rule of thumb is a line-height ratio around 1.5 for body copy and closer to 1.1 to 1.2 for large display headings.
Measure, the number of characters per line, has a comfortable range around 50 to 75 characters for body text. Wider than that and the eye struggles to track back to the start of the next line; narrower and reading feels choppy. This is a layout constraint (usually a max-width on the text container) as much as a typography one, but it's part of the same system and should be documented alongside the scale, not treated separately.

Photo by Vitaly Gariev on Pexels
Where scales actually break in production
A handful of real device situations expose a scale that was only tested on a designer's laptop:
Small Android phones with large system font settings. Android's accessibility text scaling can push a 320px-wide device's effective content area smaller than the design ever anticipated. If your smallest breakpoint assumes a fixed minimum, this is where headlines wrap unexpectedly.
Foldables and split-screen views. A foldable phone or a tablet running two apps side by side can present a viewport width that falls between your "mobile" and "desktop" assumptions. Fluid scaling handles this gracefully; fixed breakpoints don't, because there's no clean line between "phone" and "tablet" widths anymore.
Print stylesheets. If your site has a print view (invoices, articles meant to be printed, receipts), a rem-based fluid scale designed only for screen can produce oddly large or small print output unless you define separate print-specific sizing. It's easy to forget this exists until a user complains about an unreadable printed page.
"The type scales that survive three years of feature additions are the ones built as a system from day one, not the ones with the prettiest ratio on paper. We've rebuilt more typography systems from scratch than we've extended, because most weren't designed to flex." - Dennis Traina, founder of 137Foundry
Document the scale so it survives handoff
A type scale that lives only in a Figma file dies the first time a developer ships a component without checking the file. The scale needs to exist as code, as design tokens or CSS custom properties, so it's the single source of truth both design and engineering pull from. Google Fonts and most modern font-loading strategies pair well with a token-based approach, since the font-family, weight, and size decisions can all live in the same system rather than being redefined per component.
Naming matters too. heading-1 through heading-6 tells a developer nothing about when to use which. Names tied to purpose, display, heading-lg, body, caption, survive redesigns better because the underlying pixel values can change without every component needing a rename.
Accessibility isn't a separate checklist item
Contrast, scalability, and readability aren't a pass you run after the type scale is done, they're inputs into the scale itself. The W3C's accessibility guidance covers minimum contrast ratios and text resizing requirements that should shape your base size and color choices from the start, not get retrofitted in a later audit. A scale designed with a 16px functional minimum for body text and AA-compliant contrast baked into your color tokens avoids an entire category of last-minute fixes.
Variable fonts change the performance math
A traditional type system loads several static font files, one per weight and style you use: regular, bold, italic, maybe a semibold for emphasis. Each is a separate network request. Variable fonts collapse all of those into a single file that exposes weight, and sometimes width or optical size, as a continuous axis you can set with CSS rather than swapping files.
For a scale with several weights in play, this can mean loading one font file instead of four or five, which matters more on a slow mobile connection than most teams account for when they're testing on a fast office wifi network. The tradeoff is that a variable font file is often larger than any single static weight on its own, so the win depends on how many weights you actually use. If your scale only ever needs regular and bold, static files might still be lighter. If you're using five weights across headings, body, and captions, variable is very likely the better call.
Font loading strategy matters just as much as the scale itself. A font-display: swap declaration prevents invisible text while a custom font loads, trading a brief flash of the fallback font for content that's readable immediately rather than blocked. For a scale built around a specific typeface's proportions, pick a fallback font with similar x-height and character width so the swap is barely noticeable rather than jarring.
Testing the scale on real devices, not just a resized browser window
Shrinking a desktop browser window to simulate mobile catches the obvious breakpoints but misses a lot of what actually goes wrong. Real device testing catches things a simulated viewport doesn't: how a scale behaves with an actual on-screen keyboard covering half the viewport, how text wraps with a real device's default font rendering rather than your desktop OS's, and how a fluid clamp() value performs on a browser that doesn't fully support it and needs a fallback value.
A lightweight testing pass, a handful of real Android and iOS devices at different screen sizes, plus one or two with accessibility text scaling turned up, catches most of the failure modes described above before a scale ships broadly. It's a smaller investment than the rework required after a scale ships and starts collecting override CSS from every team that hits an edge case it wasn't built for.
When to bring in outside help
Most teams don't need a full design system rebuild to fix a broken type scale. Often it's a targeted audit: map the current inconsistent sizes, define a ratio, migrate the highest-traffic templates first, and let the rest follow over time rather than trying to convert everything in one sprint.
If you're weighing whether this is a project you can knock out internally or one that needs outside web development support, the deciding factor is usually less about difficulty and more about whether anyone on the team has bandwidth to own it end to end without it sliding for two quarters. Read more on our approach to design systems and web builds or browse our full services list if you're scoping a project.
A typography scale is a small piece of a design system, but it touches every screen a user sees. Getting the fundamentals right once, relative units, fluid scaling, documented tokens, accessibility baked in, saves far more time than the initial setup costs.