Making text actually vertically center with CSS

Published Updated 7 min read

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:

BehaviorUse case
noneNo trimming (default)Keep font metrics as-is
trim-startRemove space aboveReduce space above first lines
trim-endRemove space belowReduce space below last lines
trim-bothRemove space above and belowAchieve tight alignment

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

css
.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:

css
.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

RepresentsUse case
capHeight of capital lettersReference for text metrics
lhThe computed line-heightReference for line box height
exHeight of lowercase “x”For x-height-based sizing

Comparing the Approaches

text-box-trimmargin-block
LevelInlineBlock
ScopeSingle/multiple lines of inline textBlock containers
Browser supportLimited (experimental)Good (uses standard units)
How it worksTrims leading space directlyOffsets margin to achieve same effect
Best forPrecise inline text alignmentButton/badge centering, multi-line blocks
SyntaxCSS propertycalc() 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):

css
.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:

css
.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:

css
.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:

css
.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:

css
.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

css
.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

css
.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

css
.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