Building a Type Scale for Transactional Emails
Design a five-step email type scale in absolute units: paired sizes and leading, a 14px floor, mobile steps, and why fewer steps render more consistently.
A web type scale has eight or ten steps and relies on the browser to resolve relative units consistently. An email scale cannot: every value is absolute, every size must clear a mobile threshold, and every step is one more place for a client to disagree. This guide builds a five-step scale that renders identically everywhere and encodes it as tokens.
Root Cause: Every Extra Step Is Another Failure Surface
Three constraints push email scales toward fewer, larger steps.
Relative units are unreliable. rem has no dependable root in email, and em compounds through nested tables in ways that differ between engines. Every size must therefore be an absolute pixel value, which removes the automatic proportionality a web scale relies on.
There is a hard floor. Anything below roughly 14px risks being enlarged by a mobile client, so the bottom of the scale is fixed rather than chosen. A ratio-derived scale that produces 11px and 12.8px steps has two unusable values at the small end.
Leading must be paired. Because the Word engine treats leading as a floor, every size needs an explicit line-height rather than a multiplier. A size without a paired leading value is not a complete step.
The Exact Scale
Four sizes cover almost every transactional message; a fifth is occasionally useful for a large numeric display such as a verification code.
| Role | Size | Line height | Used for |
|---|---|---|---|
| Display | 32px | 40px | Verification codes, single-figure confirmations |
| Heading | 28px | 34px | The one message headline |
| Subheading | 20px | 28px | Section titles in longer messages |
| Body | 16px | 24px | All running copy and table cells |
| Small | 14px | 20px | Footers, legal text, table labels |
Each step is at least 4px from its neighbour, which keeps them visually distinguishable after a client's own rendering. The 28px-to-20px gap is deliberately larger, because a headline and a section title are the two most important levels to separate.
// type.js โ the scale as paired tokens. A size without its leading is not a
// step, so they are stored together and consumed together.
export const type = {
display: { size: "32px", line: "40px" },
heading: { size: "28px", line: "34px" },
subheading: { size: "20px", line: "28px" },
body: { size: "16px", line: "24px" },
small: { size: "14px", line: "20px" },
};
// The helper emits every declaration a text cell needs, so it is impossible
// to set a size without the leading and the MSO rule that goes with it.
export function textStyle(step, extra = {}) {
const { size, line } = type[step];
return {
fontFamily: "Arial, sans-serif",
fontSize: size,
lineHeight: line,
msoLineHeightRule: "exactly", // Outlook 2016-2021: pin the leading
...extra,
};
}
Emitting the declarations through a helper is what keeps the scale intact in practice. The realistic failure is not a developer choosing 17px deliberately โ it is a developer adding font-size: 15px to fix one cramped line and never touching the leading, which then comes from the engine.
Mobile Steps
A responsive scale needs at most one adjustment, applied through a media query, and it should increase rather than decrease. Reducing type at narrow widths pushes values toward the inflation threshold and produces exactly the enlargement you were avoiding.
<style>
@media only screen and (max-width: 480px) {
/* Headline only. Body copy at 16px is already correct on a phone, and
changing it just adds a step where two clients can disagree. */
.t-heading { font-size: 24px !important; line-height: 30px !important; }
.t-display { font-size: 28px !important; line-height: 36px !important; }
}
</style>
Only the two largest steps need adjusting. A 28px headline on a 320px screen consumes most of the width and wraps awkwardly; 16px body copy is already the right size and should be left alone.
Spacing Belongs in the Same System
A type scale on its own produces consistent sizes and inconsistent rhythm, because the space between blocks is decided separately in every template. Pairing the type scale with a small spacing scale โ and deriving the spacing values from the leading โ is what makes a set of templates look like one system.
Four spacing steps are enough for transactional mail: a tight step for related lines, a standard step between paragraphs, a section step between distinct blocks of content, and a band step around the outer edges of the message. Deriving them from the body leading keeps the vertical rhythm coherent: with 24px leading, spacing steps of 8, 16, 32 and 48 all divide evenly into it, so blocks align to the same underlying grid regardless of how many lines each contains.
The implementation constraint is that email spacing is not margin. The Word engine ignores margin on table cells, and several clients strip it from paragraphs, so vertical space is created by cell padding or by dedicated spacer rows. Both need the leading treatment described under line-height in Outlook, which is why the spacing scale and the type scale cannot sensibly be maintained apart: a spacer row is a typographic object, not a layout one.
The practical rule that follows is to express spacing only in the four named steps and never as an arbitrary pixel value. A template that needs 22px of space almost always needs one of the existing steps, and the request for a non-scale value is usually a sign that something else โ an unexpected line box, an image with a descender gap โ is consuming space that was not intended.
Pipeline Integration
Ship the scale as part of the token set rather than as a documented convention, and expose it only through the helper. A convention is followed until someone is in a hurry; a helper that emits four declarations at once is easier to use than writing them by hand, which is what makes it stick.
Add two lint rules over compiled output: fail on any font-size not in the scale, and fail on any font-size without an adjacent line-height and MSO rule. Both are trivial regex checks and both catch the realistic regression.
Validation and Deployment Checklist
Frequently Asked Questions
Why not use a modular ratio like the web?
A ratio produces fractional values, and fractional pixel sizes round differently between rendering engines โ so the visual rhythm you designed drifts by a pixel here and there across clients. Whole numbers chosen for their rendered appearance are more reliable, and with only five steps there is no real maintenance benefit to deriving them from a formula.
Should headings use a different font size on Outlook?
No. The scale should render identically across clients, and adding an Outlook-specific size introduces a second scale to maintain. What Outlook does need is the leading rule, which pins the vertical rhythm without changing any size.
How does the scale interact with the fallback font?
Sizes stay the same; the visual weight changes, because a fallback with a different x-height reads larger or smaller at the same nominal size. This is an argument for choosing a metrically close fallback rather than for adjusting the scale โ a size that changes depending on which font loaded is not a scale.
How do we migrate an existing template set onto the scale?
Map first, change second. List every distinct font size currently in use across the templates โ there are usually far more than anyone expects, often a dozen or more โ and assign each one to its nearest scale step. Most will map cleanly; the handful that do not are the interesting cases, and they are almost always one-off values introduced to fix a specific line-wrap problem that a different fix would have solved better. Apply the mapping template by template with a visual diff at each step, rather than as one sweeping change, so a step that shifts a layout is attributable to the template it broke.
Is 16px body copy too large for a dense receipt?
It is the right default even for dense content. A receipt with many line items is better served by reducing the number of visible columns on mobile than by shrinking type toward the inflation threshold. Where density is genuinely necessary, 14px is the floor, and the table should stack rather than compress further.
Related
- Email Typography and Web Fonts โ the wider type system this scale sits inside
- Preventing Font-Size Inflation in Mobile Clients โ the constraint that fixes the bottom of the scale
- Fixing Line-Height Inconsistencies in Outlook โ why every size needs a paired leading value
- Email Design Tokens and Component Libraries โ where the scale is published and versioned
โ Back to Email Typography and Web Fonts