Why All-Caps Headings Look Broken Without Custom Letter-Spacing (And How to Fix It)

Why All-Caps Headings Look Broken Without Custom Letter-Spacing (And How to Fix It)

You spend two hours getting your heading typography just right — the font weight, the size, the color. Then you slap text-transform: uppercase on it and suddenly the whole thing looks like it was typeset by someone who hates you. The letters crowd each other. The word shapes collapse. What looked clean in mixed case now looks like a ransom note that ran out of glue.

This happens to almost every developer and designer I’ve worked with, and most of them spend time tweaking font sizes or swapping typefaces trying to fix a problem that isn’t about the font at all. I’m Rohan Ratnayake, and for the past five years I’ve worked as a UI typography specialist — someone who spends an uncomfortable amount of time obsessing over how type renders across browsers, operating systems, and screen densities. I’ve watched talented developers ship headings that looked awful in production not because they couldn’t code, but because nobody ever told them the optical mechanics of what uppercase does to letter spacing.

The hard lesson I learned early on: I once handed a client a landing page where the hero heading read “OUR SERVICES” in a bold sans-serif. They loved the font. They hated how it looked. It took me two embarrassing revision cycles before I realized I hadn’t adjusted tracking at all. After adding letter-spacing: 0.12em, the heading looked like it belonged on a premium product. Zero font change. Same weight. Just tracking. That’s a lesson I don’t forget.


What Actually Happens When You Uppercase a Heading

Mixed-case text has built-in optical rhythm. Lowercase letters have ascenders and descenders — the parts of letters like “h” and “p” that stick up or dip down. Those vertical variations create natural spacing cues your eye uses to separate letters and parse words quickly.

All-caps removes every single one of those cues.

When every character sits at the same cap height with no ascenders or descenders, the eye loses those rhythm markers. Letters appear to compress toward each other even when the actual spacing hasn’t changed. The kerning pairs your type designer so carefully built — the space between “AV” or “To” in mixed case — were not designed with an all-uppercase context in mind. Most are optimized for sentence case.

ALSO READ:  Why Full-Justified Text Is an Accessibility Failure (Not Just a Design Preference)

The result is that letter-spacing: 0 on an uppercase heading isn’t neutral. It’s actively wrong.


How Much Letter-Spacing You Actually Need

This is where most articles give you a vague “it depends.” Here’s what I’ve actually measured across projects:

Heading Use CaseFont Size RangeRecommended letter-spacing
Large hero headline (display)48px–80px+0.04em to 0.06em
Section heading (H2)28px–40px0.08em to 0.12em
Subheading / label text14px–20px0.12em to 0.18em
Tiny UI caps (nav, tags)10px–13px0.15em to 0.20em

The pattern you’ll notice: the smaller the uppercase text, the more letter-spacing it needs. This is not arbitrary. At small sizes, letters sit even closer in the optical illusion created by all-caps — so you compensate harder.

Using em units here is intentional. If you use px values, a heading that looks right at font-size: 32px will look wrong the moment you adjust the size. Em-based tracking scales with the font automatically.


The Optical Illusion Nobody Talks About

There’s a specific problem with uppercase headings that isn’t about inter-letter spacing in general — it’s about the space after capital letters with flat right edges like “I”, “L”, and “E” versus those with diagonal strokes like “A”, “V”, and “W.”

When you force everything to uppercase, you dramatically increase the chance that two flat-edged capitals end up adjacent — “FILL”, “BELL”, “WELL.” These combinations look tighter than they are because two vertical strokes with no optical break create a visual clump. Standard kerning doesn’t fully resolve this in an all-caps context because kerning tables are built for the full character set, not an uppercase-only subset.

Adding global letter-spacing lifts all of those combinations uniformly, and that’s usually enough. For display text above 60px where this is especially visible, I’ll sometimes kern problem pairs manually with <span> elements if the design calls for it — but that’s a rare case.

ALSO READ:  How to Fix Orphan Words in CSS With Text-Wrap: Balance (No JavaScript Needed)

Why em Units Beat px Every Time Here

A lot of developers default to pixel values when writing letter-spacing, and it costs them later.

/* Fragile — breaks when font-size changes */
h2 {
  text-transform: uppercase;
  letter-spacing: 3px;
}

/* Scalable — tracking adjusts with size */
h2 {
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

If you’re using a type scale — and you should be — your H2 size might vary between mobile (24px) and desktop (36px). A fixed 3px gap stays 3px regardless. At 24px that feels right; at 36px it looks under-tracked. The 0.1em version gives you 2.4px on mobile and 3.6px on desktop automatically.

This becomes especially important if you’re working in a design system where type tokens feed into multiple components. One em value, one source of truth.


How Different Typefaces Respond to All-Caps

Not every font reacts the same way to uppercase treatment, and knowing this saves you from over-tracking fonts that have native spacing built in.

Font CategoryDefault Tracking Behavior in CapsAdjustment Needed
Geometric sans-serif (Futura, Circular)Tight by default, uniform strokesMedium — 0.08em to 0.12em
Humanist sans-serif (Gill Sans, Myriad)Slightly more open, varied strokesLow to medium — 0.06em to 0.10em
Transitional serif (Times, Georgia)Very tight in caps, heavy kerningHigh — 0.10em to 0.15em
Grotesque (Helvetica, Arial)Moderate, fairly neutral capsMedium — 0.08em to 0.12em
Variable fonts with optical sizingMay self-adjust at large sizesTest first before adding tracking

Some professional fonts — particularly those designed for editorial use — ship with optical size variants or built-in small-caps features that handle spacing differently. If you’re using a font with a true font-variant: small-caps feature, the spacing math changes entirely. But most heading scenarios use standard uppercase via text-transform, so the table above covers the majority of real cases.


A Practical CSS Pattern Worth Keeping

Here’s the component-level pattern I use on most projects. It’s not clever — it’s just reliable:

.heading-caps {
  text-transform: uppercase;
  letter-spacing: 0.1em;
  /* Compensate for visual right-side bleed on last letter */
  margin-right: -0.1em;
}

That margin-right: -0.1em line is something most tutorials skip entirely. When you add letter-spacing, CSS applies it after every character — including the last one. This shifts the block of text slightly to the left and adds invisible whitespace on the right edge of the element. If your heading is centered, this creates an imperceptible but real asymmetry. The negative margin cancels it out.

ALSO READ:  The CSS Line-Height Formula That Actually Stops Readers From Getting Lost

For a centered hero heading this won’t matter much. For left-aligned headings flush against a grid column edge, you’ll notice the gap. Compensate for it.


Where Most Implementations Go Wrong

The most common mistake I see in codebases is applying a single letter-spacing value across all uppercase headings regardless of size. Someone writes a utility class:

.uppercase { text-transform: uppercase; letter-spacing: 2px; }

And then uses it everywhere from a 60px hero to a 12px navigation label. The hero looks acceptable. The nav label looks like the letters are trying to escape the container.

A second common mistake is adding letter-spacing without adjusting line-height. Tighter uppercase text with increased tracking can make multi-line headings look like they have too much vertical space relative to horizontal. Most uppercase headings need a line-height between 1.1 and 1.3. Defaults of 1.5 that work fine for body copy make multi-line uppercase headings look balloon-like.

MistakeWhat It Looks LikeFix
Single px value for all sizesOvercrowded small labels, under-tracked large headingsSwitch to em units, set per-size
No margin-right compensationCentered caps drifts leftAdd margin-right: -[same value as letter-spacing]
Default line-height on multi-line capsVertical gaps feel too looseSet line-height: 1.1 to 1.3 on uppercase headings
Same tracking for all typefacesGeometric fonts look over-spaced vs. serif fontsCalibrate per font family

FAQs

Can I just set a high letter-spacing value and call it done?

You can, but over-tracking is its own problem. Once you push past 0.2em on most heading fonts, individual letters start to read as disconnected — the eye can’t group them into words efficiently. Tracking should open the text, not shatter it. Start at 0.08em and increase only if the font specifically needs it.

Does letter-spacing affect SEO or accessibility?

No, letter-spacing is a pure visual property. It has zero effect on how search crawlers read your content or how screen readers parse it. Accessibility concerns around uppercase text are about the text-transform itself — some screen readers read all-caps text character by character — not about tracking values.

Should I use letter-spacing or font-kerning to fix this?

Different tools. font-kerning: normal tells the browser to use the font’s built-in kerning table. letter-spacing adds uniform spacing between every character on top of that. For uppercase headings, you want both: keep font-kerning: normal (or don’t override it) and add letter-spacing on top. Disabling kerning with font-kerning: none while adding letter-spacing is a common mistake — you lose the font designer’s work and replace it with blunt uniform spacing.


Wrapping This Up

The cramped look of an all-caps heading isn’t a font problem or a weight problem. It’s an optical compensation problem that a single CSS property fixes — as long as you use the right unit, the right value for the size, and account for the small details like trailing space compensation.

The next time you’re setting an uppercase heading, open your browser’s dev tools, set letter-spacing to 0.1em, and adjust from there. You’ll see within ten seconds whether it needs more or less. Trust your eye over any fixed rule, but use the ranges in this article as a starting point rather than guessing from zero. The CSS Fonts specification on letter-spacing is worth bookmarking if you want the full technical behavior — particularly how it interacts with justification and bidirectional text.