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.
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 Case | Font Size Range | Recommended letter-spacing |
|---|---|---|
| Large hero headline (display) | 48px–80px+ | 0.04em to 0.06em |
| Section heading (H2) | 28px–40px | 0.08em to 0.12em |
| Subheading / label text | 14px–20px | 0.12em to 0.18em |
| Tiny UI caps (nav, tags) | 10px–13px | 0.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.
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 Category | Default Tracking Behavior in Caps | Adjustment Needed |
|---|---|---|
| Geometric sans-serif (Futura, Circular) | Tight by default, uniform strokes | Medium — 0.08em to 0.12em |
| Humanist sans-serif (Gill Sans, Myriad) | Slightly more open, varied strokes | Low to medium — 0.06em to 0.10em |
| Transitional serif (Times, Georgia) | Very tight in caps, heavy kerning | High — 0.10em to 0.15em |
| Grotesque (Helvetica, Arial) | Moderate, fairly neutral caps | Medium — 0.08em to 0.12em |
| Variable fonts with optical sizing | May self-adjust at large sizes | Test 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.
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.
| Mistake | What It Looks Like | Fix |
|---|---|---|
| Single px value for all sizes | Overcrowded small labels, under-tracked large headings | Switch to em units, set per-size |
| No margin-right compensation | Centered caps drifts left | Add margin-right: -[same value as letter-spacing] |
| Default line-height on multi-line caps | Vertical gaps feel too loose | Set line-height: 1.1 to 1.3 on uppercase headings |
| Same tracking for all typefaces | Geometric fonts look over-spaced vs. serif fonts | Calibrate 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.

