Email Typography and Web Fonts: Stacks, Metrics, and Client-Safe Type Scales
Build type systems that survive email clients: metric-matched fallback stacks, MSO line-height control, mobile font inflation, and a scale that renders identically everywhere.
Typography is where email design most often quietly falls apart. The layout survives, the colours survive, and then a headline set in a webfont renders in Times New Roman two sizes too large, pushing a two-line heading onto three lines and breaking the spacing of everything beneath it. This section covers how type is actually resolved in email clients, and how to build a scale that produces the same rhythm whether or not your font loads.
The problem is that email has no single font-loading model. One client downloads a webfont, one uses a locally installed copy if it happens to exist, one ignores @font-face completely, and one rewrites your font size on the way to the screen. A type system that assumes any of those behaviours in isolation will break in the others.
How Type Is Resolved, Client by Client
Four distinct resolution paths exist, and every font declaration you write travels down one of them.
WebKit clients — Apple Mail on macOS and iOS Mail — support @font-face with a remote src, resolved from the embedded style block. They are the only mainstream email clients where a genuine webfont reliably arrives.
Gmail strips remote @font-face sources but honours local(), so a family already installed on the device resolves; anything else falls through to the next entry in the stack. In practice this means Gmail shows your brand font to the small population who happen to have it installed and the fallback to everyone else.
The Word engine in Outlook 2016 through 2021 on Windows ignores @font-face entirely and, worse, ignores the rest of a font-family stack once it encounters a name it does not recognise — falling back to Times New Roman rather than to the next entry. This is the failure mode that produces serif headings in an otherwise sans-serif design.
Server-side rewriters such as Outlook.com and Yahoo keep the declaration but rewrite selectors around it, so a font applied through a class may or may not survive; a font applied inline always does.
Core Implementation: A Stack That Degrades Predictably
The fix for the Word engine is an MSO conditional that overrides the family before the engine ever sees the name it will choke on. Everything else is served by a carefully ordered stack.
<style>
/* Layer 1 — the webfont, for the clients that will actually fetch it.
local() first avoids a network round-trip when the family is installed
(Gmail resolves this and nothing else). */
@font-face {
font-family: 'BrandSans';
src: local('BrandSans'),
url('https://cdn.example.com/brandsans-400.woff2') format('woff2'),
url('https://cdn.example.com/brandsans-400.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap; /* Apple Mail: paint the fallback, swap on arrival */
}
/* Layer 2 — the stack every other client walks. Ends in a generic family so
no client is left choosing for itself. */
.body-text {
font-family: 'BrandSans', -apple-system, 'Segoe UI', Roboto,
'Helvetica Neue', Arial, sans-serif;
}
</style>
<!--[if mso]>
<style>
/* Outlook 2016-2021 (Word engine): it cannot render BrandSans and will fall
back to Times New Roman rather than to the next stack entry, so name a
font it definitely has. Arial is present on every Windows install. */
.body-text, td, th, p, h1, h2, h3, a, span {
font-family: Arial, sans-serif !important;
}
</style>
<![endif]-->
The local() entry first is not a micro-optimisation. It is the only reason a Gmail recipient with the family installed sees the brand font at all, and it saves a network fetch for the WebKit clients where the font is already present.
The other half of the fix is choosing a fallback whose metrics match the webfont. When the fallback is wider or has a different x-height, the swap reflows text the reader is already looking at, and every line-break decision changes — a headline that fitted on two lines in your design occupies three in the fallback. Comparing cap height and average character width between the brand font and each candidate fallback, then picking the closest, keeps the layout stable in both states. The mechanics of that comparison are covered in detail under web font fallbacks for Apple Mail.
Line Height Is the Second Failure
Font family gets the attention; line height causes as many broken layouts. The Word engine computes leading from the font's own metrics and ignores a unitless line-height, so a value like 1.5 produces whatever the engine feels like — usually tighter than intended, occasionally much looser.
<!-- Set line-height in absolute units AND give the Word engine its own
directive. mso-line-height-rule:exactly stops it recomputing from the
font metrics; without it, the px value below is treated as a minimum. -->
<td style="font-family:Arial,sans-serif;
font-size:16px;
line-height:24px;
mso-line-height-rule:exactly;">
Body copy that keeps a 24px rhythm in Outlook 2016-2021 as well as everywhere else.
</td>
Two rules follow from this. Always express line-height in pixels rather than as a unitless multiplier, and always pair it with mso-line-height-rule: exactly on any element carrying text. The multiplier is more maintainable on the web precisely because browsers recompute it; in email that recomputation is the bug.
Mobile Font Inflation
Both iOS and the Gmail Android app enlarge text they judge too small to read, and the enlargement is not proportional — it applies per block, so one paragraph grows and its neighbour does not, breaking a carefully built scale.
The controls are per-platform. -webkit-text-size-adjust: 100% on the body container stops iOS resizing. Android's WebView respects text-size-adjust, but the more reliable protection is simply never setting a body size below 14px, since inflation is triggered by a size threshold rather than by anything you can disable. The same applies to a responsive layout's mobile breakpoint: increase type at narrow widths deliberately rather than letting the client do it for you.
| Client | Webfont support | Line-height handling | Inflation behaviour |
|---|---|---|---|
| Apple Mail (macOS) | Full @font-face |
Honours all units | None |
| iOS Mail | Full @font-face |
Honours all units | Enlarges below ~13px unless text-size-adjust is set |
| Gmail (web) | local() only |
Honours all units | None |
| Gmail (Android app) | local() only |
Honours all units | Enlarges small text per block |
| Outlook 2016/2019/2021 | None — falls back to serif | Requires mso-line-height-rule |
None |
| Outlook 365 (WebView2) | Full @font-face |
Honours all units | None |
| Samsung Email | local() only |
Honours all units | Enlarges small text |
Building the Scale
A type scale for email should be shorter than one for the web — four or five steps, all in absolute pixels, each paired with an exact line height. Fewer steps means fewer places for a client to disagree with you, and absolute values remove the recomputation that causes drift.
Set the scale once as a set of design tokens and apply them inline through your templating layer rather than through classes, so the rewriters cannot strip them. The token approach also keeps the scale consistent across templates, which is what shared component libraries are for.
- Define the scale in absolute pixels — for example 28/20/16/14/12 — and pair each size with a fixed line height.
- Pick the fallback by metrics, not by aesthetics, and verify a two-line headline stays two lines in both states.
- Attach the MSO conditional override naming a font present on every Windows install.
- Set
mso-line-height-rule: exactlyon every element that carries text, not only on paragraphs. - Apply sizes inline through the template layer so server-side rewriters cannot drop them.
- Guard against inflation with
-webkit-text-size-adjustand a 14px floor for body copy. - Verify in both font states — with the webfont loaded and with it blocked — as a standing item in your rendering QA pass.
Debugging: Named Symptoms, Cause, and Fix
Symptom: headings render in a serif face in Outlook on Windows only. Cause: the Word engine hit an unknown family name and fell back to Times New Roman instead of walking the stack. Fix: add the MSO conditional block naming Arial or another guaranteed font, and confirm it targets headings as well as body elements.
Symptom: line spacing is tighter in Outlook than everywhere else. Cause: a unitless line-height, recomputed from font metrics. Fix: convert to pixels and add mso-line-height-rule: exactly.
Symptom: one paragraph is noticeably larger than the rest on an Android phone. Cause: per-block text inflation triggered by a size below the client's readability threshold. Fix: raise the body size to at least 14px; the enlargement is not disabled by any property on that platform.
Symptom: the headline wraps to three lines for some recipients and two for others. Cause: a fallback font with different metrics from the webfont. Fix: choose a metrically closer fallback, or set the headline size so the longest expected string fits on two lines in the widest candidate.
Symptom: font sizes disappear entirely in Outlook.com. Cause: sizes applied through a class in the embedded style block, which the rewriter dropped. Fix: apply type inline at the element level; keep the style block for the @font-face rule and media queries only.
Validation and Deployment Checklist
Measurement, Colour, and the Rest of the Type System
Family, size and leading are the properties people remember to set. Three more decide whether the result is actually readable in an inbox, and each has an email-specific constraint that does not exist on the web.
Measure — the number of characters on a line — is fixed by your container width rather than by a CSS property, because email layouts are built at a known pixel width instead of a fluid one. At the conventional 600px content width with 16px body copy, a single-column paragraph runs to roughly 75 characters, which is at the upper end of comfortable. Adding horizontal padding is the lever: 24px on each side brings the measure down to a more readable range without changing the type size. Where a template uses two columns, each column is around 35 characters wide, which is too narrow for body copy and should be reserved for labels and short values. This is one of the places where a responsive layout's stacking behaviour matters for typography rather than for structure: a two-column row that stacks on mobile goes from an unreadably narrow measure to a comfortable one, so the mobile rendering is often the better-typeset of the two.
Colour and contrast interact with type size through the WCAG thresholds. Body copy needs 4.5:1 against its background; text at 18.66px bold or 24px regular qualifies as large and needs only 3:1. The practical consequence in email is that muted secondary text — the grey used for timestamps, disclaimers and footer copy — is the single most common accessibility failure, because the shade chosen to look unobtrusive against white usually lands between 3:1 and 4.5:1. Fix it by darkening the muted colour rather than by enlarging the text, since footers are rarely a place where larger type is wanted. The same pairing has to hold in dark mode, where a muted grey that reads correctly on white becomes low-contrast against a dark surface; the paired-value approach described under dark mode email CSS is what keeps both states compliant.
Weight and emphasis are constrained by what the fallback family actually contains. A brand font may ship six weights; Arial has two. A design that distinguishes a heading from a subheading by weight alone therefore collapses into a single appearance in the Word engine, where both render as bold. Build hierarchy from size and spacing first, with weight as a secondary signal, so the structure survives when the weight distinction does not. The same reasoning rules out relying on a light weight for emphasis of any kind: font-weight: 300 renders as regular wherever the family lacks that weight, which is most of the time.
The three properties compound. A two-column row containing 14px muted grey text at a 35-character measure is simultaneously too narrow, too small and too low-contrast — and each of those is individually within the range a designer might reasonably choose. Reviewing them together, rather than one property at a time, is what catches the combination.
Frequently Asked Questions
Is it worth shipping a webfont at all when so few clients load it?
It depends on your audience mix. Apple Mail and iOS Mail together account for a large share of consumer opens, and those clients do render the font — so a brand that matters visually gets real value. What is never worth it is designing for the webfont state: build the layout so it is correct in the fallback, then treat the webfont as an enhancement that a majority of recipients will not see.
Why does the Word engine fall back to a serif rather than to the next font in my stack?
Because it does not implement font-family as a prioritised list in the way browsers do. When it encounters a family it cannot resolve, it stops evaluating and applies its own document default, which is a serif face. This is why the conditional override is mandatory rather than defensive — no ordering of the stack can produce the right result on its own.
Can I use variable fonts in email?
Only in the WebKit clients, and the file-size cost is usually not justified. A variable font carries every weight in one file, which is efficient on the web where you use many; email templates typically need two, so two static WOFF2 files are smaller than one variable font. The weights also have to be declared as separate @font-face rules for clients that do not understand variable axes.
Should I set font size on the table cell or on a wrapper element?
On the cell. Inheritance is unreliable in email — several clients reset font properties on table elements — so the safe pattern is to declare family, size and line height on every td that contains text, rather than relying on a parent to pass them down. The repetition is verbose by hand, which is exactly why it belongs in a component or partial.
How do I test what the fallback actually looks like?
Block the font's domain in your preview environment, or temporarily point the src at a URL that returns a 404. Both force the fallback path in clients that would otherwise fetch the file, so you see the layout the majority of your recipients get. Doing this as a deliberate second pass catches metric mismatches that never appear in a normal preview.
Do email clients support font-display?
Apple Mail honours it, which is the only place it matters, since it is the only client fetching a remote font. Use swap so the fallback paints immediately and the webfont replaces it on arrival — the alternative, invisible text while the font downloads, is a worse experience in a message the reader opened deliberately.
Related
- Web Font Fallbacks for Apple Mail — the metric-matching method in full, with the loading pipeline
- Responsive Email Layouts — where the mobile type step belongs in the breakpoint strategy
- Outlook Rendering Fixes — the wider set of Word-engine overrides this shares a conditional block with
- Inline CSS Automation — how these declarations survive the inliner and reach the element
← Back to Mastering Email HTML & CSS