Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vertical alignment based on x-height

Tags:

When trying to center content in a container CSS-Tricks has a great guide. However when trying to vertically center some text that's just slightly smaller than its container, I think a different way of vertically centering text might be preferable. Instead of using the entire height of the font, I would rather center it based on the x-height of the font (basically the height of a lowercase x)

Visual explanation of x-height

And see this example where red is based on the entire height and green is based on the x-height

Difference between vertically centering

The only option I could come up with is to add a pseudo element to the text with the same height as the container and to use vertical-align: middle to it.

.pseudo {
  white-space: nowrap;
}

.pseudo:before {
  content: '';
  display: inline-block;
  vertical-align: middle;
  height: 100px;
  width: 0;
}

This works, but unfortunately only for a single line. I was wondering if anyone else tried to solve this issue and if perhaps there are best practices to follow? I am especially interested using as little "magic" numbers as possible and if there is a good solution for the multi line variant.

See Codepen for an example on why I want to center it based on the x-height, and my solution.

like image 541
ckuijjer Avatar asked Sep 14 '14 17:09

ckuijjer


1 Answers

The differece between text center position and the small letters center is equal to (ascender height - x-height - descender height)/2 (basically we need to increase somehow the descender height to make it equal to ascender height - x-height to move the geometric center of the line box to the position of the small letters center). From these 3 unknowns, only x-height is available for CSS (via ex unit). Other font metrics can't be read and remain kind of 'magical numbers', so it's possible only to choose the a specific value for each specific font. But with this 'font-specific magic number' you can center any number of lines - by giving the inner element display:inline-block and assigning the magic value to its padding-bottom.

It seems impossible to get the needed value from the font metrics in pure CSS. Such vertical-align values as text-top/text-bottom can give the position of ascender or descender, but only one of them, exotic values like sub seem to be completely arbitrary, and I found no possibility to 'measure' the difference between two font metrics for one element.

My most successful attempt was the way to move the line (or lines) by half of the needed difference, making 'hybrid' centering (neither caps nor lowercase letters are centerd precisely, but 'optically' the text may look even better centered). This can be done by another pseudo element added to the last line, that has the height of the line box, but its aligned with the center of small letters:

.blue:after {
  content: ':'; /* must contain text to get the auto height of the line box */
  display: inline-block;
  vertical-align: middle;
  width: 0; /* making pseudo elenent invisible */
  overflow: hidden;
}

Edited CodePen example with the result (I didn't hide pseudo elements there for visualization).

For centering the inline-block itself, any approach can be used, I choose the approach with second helper pseudo element that always has 100% height of the container, so no more magic numbers are needed.

Hope it helps:)

like image 73
Ilya Streltsyn Avatar answered Sep 23 '22 18:09

Ilya Streltsyn