Skip to content

How to Vertically Center Text in CSS Properly

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:

  • Cap height: The height of uppercase letters (like “A”, “B”, “M”)
  • Baseline: The invisible line that letters sit on
  • x-height: The height of lowercase letters like “x” (from baseline to top)
  • Ascent: Space above the baseline (includes cap height plus room for accents)
  • Descent: Space below the baseline (for descenders like “g”, “y”, “p”)
  • Line-height box: The total vertical space allocated for a line of text

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:

  • Above the text: From the top of the line-height box to the top of the cap height
  • Below the text: From the baseline to the bottom of the line-height box

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:

  • cap: Uses the cap height (top of capital letters) as the edge
  • alphabetic: Uses the alphabetic baseline (the line letters sit on) as the edge
  • ex: Uses the x-height (top of lowercase “x”) as the edge
  • text: Uses the em-box (default font metrics box) as the edge

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:

  • font-size: 16px
  • line-height: 1.5 (so 1lh = 24px)
  • cap height: 0.7em (so 1cap ≈ 11.2px)

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):

  • You want the most semantically correct approach
  • You’re working with inline text or single logical units
  • You can use @supports for feature detection
  • You want cutting-edge browsers to get the best experience

Use margin-block only:

  • You need reliable browser support today (good support for cap/lh units)
  • You’re styling buttons, badges, or heading containers
  • You’re comfortable with calc() math
  • You’re working at the block level

Progressive enhancement pattern (recommended):

  • Start with margin-block as the fallback
  • Use @supports (text-box-trim: trim-both) to apply text-box-trim when available
  • Always cancel margin-block inside the @supports block to prevent double adjustment

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

  • text-box-trim: Limited support (experimental), check caniuse.com
  • Font-relative units (cap, lh): Good support, check MDN
  • margin-block: Good support (part of CSS Logical Properties)