Font Metrics: Why Text Won't Center in Buttons
Invisible bounding boxes in fonts cause alignment issues—learn to fix them with CSS overrides
Center text in a button with display: flex and align-items: center—yet the text sits noticeably off-center. The culprit? Font internal metrics: invisible bounding boxes built into every font file that browsers use to calculate vertical positioning.
The Hidden Bounding Box Problem
Every font defines vertical metrics inside its file format—values determining where baselines sit, how much space ascenders (like 'h') and descenders (like 'g') get, and crucially, how much empty space browsers add above and below text. These metrics create an invisible content box that's typically larger than your actual letterforms.
/* This button looks vertically misaligned */
button {
display: flex;
align-items: center; /* Centers the font's content box, not the letters */
padding: 12px 24px;
font-size: 16px;
}
/* The 16px font has a content box of ~24px due to font metrics */
/* Visual letters only occupy ~12px, creating asymmetric space */Why Fonts Have Inconsistent Metrics
Font vertical metrics are stored in three different tables within font files (OpenType/TrueType):
hhea(Horizontal Header): Used by macOS/SafariOS/2 typo(Typographic metrics): Used by Linux, sometimes WindowsOS/2 win(Windows metrics): Used by older Windows applications
Different rendering engines read different tables. A font might render perfectly centered in Chrome but sit high in Safari—same CSS, different internal metrics being referenced.
Real Example: Google Fonts
Many Google Fonts ship with generous vertical metrics for legacy compatibility. Roboto at font-size: 16px creates a content box of roughly 24px, with only ~11px occupied by actual letterforms. The remaining 13px is split asymmetrically (often 60/40) above/below the baseline, creating that "text sits too high" appearance.
CSS Solutions: Overriding Font Metrics
1. Manual Adjustment (Universal Support)
/* Quick fix: negative margin or transform */
button {
padding: 12px 24px;
}
button span {
display: block;
transform: translateY(1px); /* Push text down optically */
}
/* Alternative: line-height hack */
button {
line-height: 1; /* Removes extra vertical space */
padding: 14px 24px; /* Compensate with more padding */
}2. Font Metric Overrides (Chrome 87+, Firefox 89+)
Modern browsers support overriding font metrics directly in @font-face:
@font-face {
font-family: 'Roboto';
src: url('/fonts/roboto.woff2') format('woff2');
/* Override internal metrics to reduce content box */
ascent-override: 92%; /* Reduce ascender space */
descent-override: 24%; /* Reduce descender space */
line-gap-override: 0%; /* Remove line gap */
}
/* Now Roboto centers more predictably */
button {
font-family: 'Roboto', sans-serif;
display: flex;
align-items: center; /* Actually centers now */
}Calculate overrides using tools like Capsize which analyzes font files and generates precise override values.
3. text-box-trim (Experimental)
CSS Working Group introduced text-box-trim and text-box-edge (formerly leading-trim/text-edge) to trim excess space programmatically:
/* Trim to cap height and baseline */
.heading {
text-box-trim: both;
text-box-edge: cap alphabetic;
}
/* Only trim top (preserve descender space) */
.body {
text-box-trim: start;
text-box-edge: cap alphabetic;
}Browser support: Limited (Safari 17.4+, Chrome behind flag). Check Can I Use before production use.
Optical Alignment: The Visual Layer
Even with perfect metrics, mathematical center ≠ optical center. Human perception requires additional adjustments:
- Uppercase text appears larger than lowercase at the same font-size—reduce by 5-10%
- Round letters (O, C, G) need to overshoot alignment boundaries slightly
- Pointed letters (A, V, W) need extra negative margin on sides
- Icons mixed with text typically need 1-2px downward adjustment
/* Optical adjustments for icons */
.icon-text {
display: inline-flex;
align-items: center;
gap: 8px;
}
.icon-text svg {
transform: translateY(0.1em); /* Subtle downward shift */
}Debugging Font Metrics
Chrome DevTools
- Inspect element → Computed tab → Rendered Fonts
- Check "Show rulers" to visualize content box vs letterforms
- Use
* { background: rgba(255,0,0,0.1); }to see all boxes
Font Analysis Tools
- Capsize: Generates override values for any Google Font
- FontDrop.info: Drag-drop font files to inspect internal metrics
- Wakamai Fondue: Detailed metric visualization with tables
Production Recommendations
- Set line-height: 1 for UI text (buttons, badges, tabs) to eliminate metric-based spacing
- Use ascent-override/descent-override for custom fonts loaded via @font-face
- Apply optical adjustments to critical UI elements (CTAs, navigation)
- Test across browsers—Safari uses different metrics than Chrome
- Document font stack decisions with metric characteristics for team consistency
Vertical alignment isn't just "eyeballing it"—it's understanding that fonts carry invisible baggage from decades of typesetting history, and modern CSS gives you the tools to override those defaults.
Advertisement
Explore these curated resources to deepen your understanding
Official Documentation
Tools & Utilities
Further Reading
Deep dive CSS: font metrics, line-height and vertical-align
Comprehensive technical breakdown of font metrics and CSS rendering
Leading-Trim: The Future of Digital Typesetting
Microsoft Design article on text-box-trim and leading control
Fixing Vertical Metrics in Web Fonts
Practical guide to fixing font metric inconsistencies
Related Insights
Explore related edge cases and patterns
Advertisement