Reputation: 13814
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)
And see this example where red is based on the entire height and green is based on the x-height
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.
Upvotes: 25
Views: 11008
Reputation: 1035
2025 Update: There is a draft for a new CSS property text-box
, which is implemented in Chrome 133 and Safari 18.2.
.ex-trim {
text-box: trim-both ex alphabetic; /* trim to x-height */
}
.cap-trim {
text-box: trim-both cap alphabetic; /* trim to cap height */
}
See https://developer.chrome.com/blog/css-text-box-trim for a detailed explanation.
Upvotes: 1
Reputation: 6733
Ilya Streltsyn's answer (the accepted one) is amazing. Exactly the kind of thing I like about CSS - if you know the mechanics well enough, you can accomplish anything.
I have generalized the accepted answer into a reusable class, tested on Chrome, Firefox and Edge. I have also fixed issues with how it sits in the document flow (the container would carry extra width from the ::after
elements, and would appear lower than sibling elements). You can easily use the class as follows:
<any class="x-height">
<span class="x-height">
Centered text
</span>
</any>
And below is the Sass source (Also check it out on Codepen):
%x-height {
content: 'x';
display: inline-block;
vertical-align: middle;
width: 0;
}
.x-height {
vertical-align: bottom;
&::after {
@extend %x-height;
content: '';
height: 100%;
}
> .x-height {
display: inline-block;
vertical-align: baseline;
margin-left: -1ch;
margin-right: calc(-1ch / 2);
white-space: nowrap;
@at-root {
@-moz-document url-prefix() {
& {
margin-left: auto;
margin-right: auto;
&::before { display: none; }
}
}
}
&::before, &::after {
@extend %x-height;
visibility: hidden;
}
&::after {
width: 1ch;
margin: 0 -1ch;
}
}
}
Upvotes: 1
Reputation: 13556
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:)
Upvotes: 13
Reputation: 2525
You probably need one of these jQuery plugins:
Upvotes: 0
Reputation: 180
Use this css
.outer {
display: table;
width: 200px;
height: 200px;
background-color:blue;
}
.inner {
display: table-cell;
vertical-align: middle;
/*You can align center if you want as well*/
/*text-align:center;*/
}
.box {
position: relative;
display: inline-block;
background: orange;
color: black;
font-size: 60px;
background-color: yellow;
}
And use this markup. So whatever is in the "box" div will be centered. So it is just about adding an outer to act as table and inner to act as cell so you can use the vertical align middle for multiple lines.
<div class="outer">
<div class="inner">
<div class="box">
Texty</br>
Texty
</div>
</div>
</div>
here is a codepen
Upvotes: 2
Reputation: 21004
I think the only way to get the wanted result is to use Dinamyc CSS (DCSS).
First, you will need to create a function in your website that will retrieve the height of the text (to lower case).
Second, you will need to output the css with a static position which is in reality dynamic since it is printed by your dynamic code.
Here an example pasted from this link http://www.phpsnaps.com/snaps/view/get-text-height-width/ on how to retrieve your text height in PHP :
define("F_SIZE", 8);
define("F_FONT", "arial.ttf");
function get_bbox($text){
return imagettfbbox(F_SIZE, 0, F_FONT, $text);
}
function text_height ($text) {
$box = get_bbox($text);
$height = $box[3] - $box[5];
return $height;
}
function text_width ($text) {
$box = get_bbox($text);
$width = $box[4] - $box[6];
return $width;
}
And then you would echo your (x)HTML with CSS somehow like that :
echo "<span style=\"YourStyleProperty=" . **Your line height / 2 + your text height / 2 (Hint: use the PHP or equivalent if other language)** . ";\"
For more information on the imagettfbbox function :http://php.net/manual/fr/function.imagettfbbox.php
Feel free to post if you are having trouble finalizing the code, I will be glad to help if you show some efforts :).
For more info on DCSS and maybe better ideas/example don't hesitate to google DCSS.
Upvotes: 2
Reputation: 66
Sorry can't comment.
How about this:
.green {
color: #6c6;
background-color: #cfc;
vertical-align: -16%;
line-height: 60px;
}
http://jsfiddle.net/hcn25psh/3/
and some info which might help:
http://www.w3schools.com/cssref/pr_pos_vertical-align.asp
Upvotes: 2