Skip to content

How to Vertically Center Text in CSS Properly

Published

Text is surrounded by invisible space due to font metrics. This “leading space” causes alignment challenges: text doesn’t visually center with the line-height, buttons appear slightly off-center, and spacing feels inconsistent. Two CSS properties address this problem at different levels: text-box-trim for inline text and margin-block for block-level layouts.

The Problem: Leading Space in Fonts

Top of line-height Ascent Cap height Baseline Descent Bottom of line-height = half leading space July

Every font has metrics that define space above and below the actual visible letters:

What is leading space?

Imagine text like “Hello” in a box. Visually, you see the letters taking up a certain height. But the browser allocates extra invisible space:

This invisible padding is “leading space” (pronounced “led-ing”, from traditional typesetting where lead strips created space between lines).

Visual problem: Text doesn’t appear centered even though it technically is. Buttons look misaligned. Spacing feels off because the leading space throws off visual balance.

Solution 1: text-box-trim (Inline Level)

The experimental text-box-trim property removes leading space for inline text, making it possible to align text precisely.

Practical Use

text-box-trim
.text {
  font-size: 32px;
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic;
}
Without trim:
Text
With trim-both:
Text

Your browser doesn’t support text-box-trim yet. The example above won’t show the trimming effect. Consider using the margin-block technique below as a fallback.

How It Works

text-box-trim trims the leading space above and/or below text by specifying which edges to trim:

Behavior Use case
none No trimming (default) Keep font metrics as-is
trim-start Remove space above Reduce space above first lines
trim-end Remove space below Reduce space below last lines
trim-both Remove space above and below Achieve tight alignment

Combined with text-box-edge to specify which font metrics to use as boundaries:

.text {
  text-box-trim: trim-both;
  text-box-edge: cap alphabetic; /* Trim from cap height to alphabetic baseline */
}

Understanding text-box-edge values:

The combination cap alphabetic means “trim everything above cap height and below the baseline,” which gives you the tightest possible fit around the visual letters.

Solution 2: margin-block (Block Level)

The margin-block property, combined with font-relative units (cap and lh), achieves the same effect as text-box-trim but at the block level and with much better browser support.

Practical Use

.text {
  font-size: 32px;
  line-height: 1.75;
  margin-block: calc(0.5cap - 0.5lh);
}
Without margin-block:
Text
With margin-block:
Text
vs text-trim-both:
Text

How It Works

The formula calc(Xcap - Ylh) centers content by offsetting the cap height against the line-height:

.centered-button {
  margin-block: calc(0.5cap - 0.5lh);
}

Let’s say you have text with:

Without any adjustment:

  1. The line-height box is 24px tall
  2. The visual cap height is only ~11.2px
  3. The browser centers the 11.2px caps within the 24px box
  4. This leaves ~6.4px above and ~6.4px below the caps

But you want to center based on the visual caps (11.2px), not the full line-height (24px).

The formula calc(0.5cap - 0.5lh) does this:

  1. 0.5cap = half the cap height = ~5.6px
  2. 0.5lh = half the line-height = 12px
  3. 0.5cap - 0.5lh = 5.6px - 12px = -6.4px

This negative margin pulls the text up by 6.4px, removing the extra space above the caps and visually centering the content. You’re centering the visual caps, not the line-height box.

Font-Relative Units

Represents Use case
cap Height of capital letters Reference for text metrics
lh The computed line-height Reference for line box height
ex Height of lowercase “x” For x-height-based sizing

Comparing the Approaches

text-box-trim margin-block
Level Inline Block
Scope Single/multiple lines of inline text Block containers
Browser support Limited (experimental) Good (uses standard units)
How it works Trims leading space directly Offsets margin to achieve same effect
Best for Precise inline text alignment Button/badge centering, multi-line blocks
Syntax CSS property calc() with font-relative units

When to Use Each

Use text-box-trim (with margin-block fallback):

Use margin-block only:

Progressive enhancement pattern (recommended):

Common Mistakes

Combining text-box-trim and margin-block without @supports

Don’t apply both techniques directly—they will stack and create double the adjustment! Browsers that support text-box-trim also support cap/lh units. Always use @supports (text-box-trim: trim-both) to detect support and cancel margin-block when text-box-trim is available.

Using margin-block without considering line-height

The formula only works when you have a defined line-height. If line-height: normal, the lh unit will vary by font and may not give expected results. Always set an explicit line-height when using this technique.

Forgetting that cap height varies by font

Different fonts have different cap heights relative to their em-box. A formula that works perfectly for one font may need adjustment for another. Test with your actual font choice.

Applying to multi-line text

While margin-block can work with multi-line text, the effect is most predictable for single lines. For multi-line content, the leading space between lines also affects visual balance.

Progressive Enhancement

Use @supports to prevent stacking both approaches (browsers that support text-box-trim also support cap/lh units):

.button-text {
  /* Baseline: Works in all browsers */
  line-height: 1.2;

  /* Fallback: Use margin-block for browsers without text-box-trim */
  margin-block: calc(0.5cap - 0.5lh);
}

/* Only apply text-box-trim if supported, and cancel margin-block */
@supports (text-box-trim: trim-both) {
  .button-text {
    margin-block: 0; /* Cancel the margin-block trick */
    text-box-trim: trim-both;
    text-box-edge: cap alphabetic;
  }
}

This ensures:

  1. Browsers without cap/lh support show normal text (no adjustment)
  2. Browsers with cap/lh but not text-box-trim use the margin-block trick
  3. Browsers with text-box-trim use that instead and cancel margin-block to prevent double adjustment

Real-World Scenarios

Button text centering

Challenge: Button text appears off-center vertically because of leading space, especially noticeable with taller buttons or larger font sizes.

Solution:

.button {
  display: inline-flex;
  align-items: center;
  padding: 0.75em 1.5em;
  line-height: 1.2;
}

.button-text {
  margin-block: calc(0.5cap - 0.5lh);
}

Badge or pill components

Challenge: Small badges with tight padding show uneven spacing due to leading space.

Solution:

.badge {
  display: inline-block;
  padding: 0.25em 0.5em;
  line-height: 1;
  font-size: 0.875rem;
  margin-block: calc(0.5cap - 0.5lh);
  border-radius: 9999px;
}

Hero heading alignment

Challenge: Large hero headings need to align precisely with other design elements (images, lines, etc.).

Solution:

.hero-heading {
  font-size: 4rem;
  line-height: 1.1;
  /* Fallback for browsers without text-box-trim */
  margin-block: calc(0.5cap - 0.5lh);
}

@supports (text-box-trim: trim-both) {
  .hero-heading {
    margin-block: 0;
    text-box-trim: trim-both;
    text-box-edge: cap alphabetic;
  }
}

Icon + text alignment

Challenge: When placing icons next to text, the icon aligns to the line-height box, not the visual letters.

Solution:

.icon-text-container {
  display: flex;
  align-items: center;
  gap: 0.5em;
}

.icon-text-container .text {
  margin-block: calc(0.5cap - 0.5lh);
}

This ensures the text visually aligns with the center of the icon.

Interactions with Other Alignment Properties

With flexbox align-items

.flex-container {
  display: flex;
  align-items: center; /* Centers based on flex item height */
}

.flex-item-text {
  margin-block: calc(0.5cap - 0.5lh); /* Further adjusts for visual centering */
}

align-items: center centers the entire flex item (including its line-height box). Adding margin-block then centers the visual text within that already-centered box. This gives you pixel-perfect alignment.

With vertical-align

.inline-container {
  vertical-align: middle; /* Aligns to the middle of the line-height box */
}

.inline-container .text {
  text-box-trim: trim-both; /* Trims leading space for visual alignment */
  text-box-edge: cap alphabetic;
}

vertical-align: middle aligns based on the line-height box. text-box-trim then removes the leading space, making the visual alignment match the technical alignment.

With grid align-items

.grid-container {
  display: grid;
  align-items: center; /* Centers based on grid cell height */
}

.grid-item-text {
  margin-block: calc(0.5cap - 0.5lh); /* Adjusts for visual centering */
}

Grid alignment works on the box level, margin-block works on the visual level.

Browser Compatibility

Keep reading