EdgeCases Logo
Nov 2025
CSS
Deep
7 min read

Font Metrics: Why Text Won't Center in Buttons

Invisible bounding boxes in fonts cause alignment issues—learn to fix them with CSS overrides

css
typography
fonts
alignment
font-metrics
edge-case
performance

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 */
Loading visualizer...

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/Safari
  • OS/2 typo (Typographic metrics): Used by Linux, sometimes Windows
  • OS/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

  1. Inspect element → Computed tab → Rendered Fonts
  2. Check "Show rulers" to visualize content box vs letterforms
  3. 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

  1. Set line-height: 1 for UI text (buttons, badges, tabs) to eliminate metric-based spacing
  2. Use ascent-override/descent-override for custom fonts loaded via @font-face
  3. Apply optical adjustments to critical UI elements (CTAs, navigation)
  4. Test across browsers—Safari uses different metrics than Chrome
  5. 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

Related Insights

Explore related edge cases and patterns

CSS
Surface
Dynamic Fonts in Web Development
5 min
CSS
Surface
Font Synthesis: Avoiding Fake Bold and Italic
6 min

Advertisement