4

My usual approach to vertically center text is to use line-height equal to the container's height.

Thus, the container has

height: 60px;
line-height: 60px;

and the child elements have

line-height: 60px;

That works. But if you increase the font-size above 1em, that messes it up. Worse, browsers are inconsistent in how they do it!

Here's a JSFiddle demo: http://jsfiddle.net/tgv2rx7f/7/. Notice that in Firefox the big "A." is too close to the top of the container. It doesn't look so bad in the demo, but on my actual website it's very noticeable and distracting. If you fix it in Firefox, then it's too low in Chrome.

I can't seem to get this to work consistently no matter what I do. I've played with different vertical-align values, top, text-top, middle... no dice. I can get it to work if I change the container to display:inline, but in my layout it needs to be block or inline-block.

PS, I can't use flex box.

Edit: this is what I'm seeing, both in my actual webpage (blue) and in the JSFiddle (green).

Chrome:

  • Chrome2 Chrome1

Firefox:

  • Firefox2 Firefox1

Edit 2: Thanks to andyb for pointing out the fact that using ems will change the size of a container set with px. It also helped bring to light another complication, namely that browsers treat font sizes and heights differently, but that seemed beyond the scope of this question, so I created a new question on that topic (here) and marked andyb's answer as accepted.

Community
  • 1
  • 1
brentonstrine
  • 21,694
  • 25
  • 74
  • 120
  • Is it possible to use `display:table-cell` for the container? http://jsfiddle.net/tgv2rx7f/8/ – cautionbug Dec 04 '14 at 20:34
  • Unfortunately, no, that breaks the layout. – brentonstrine Dec 04 '14 at 20:59
  • i'm not sure what you're seeing with the large A being out of alignment... http://jsfiddle.net/tgv2rx7f/39/ i see A & bbbb aligned to the middle of the box. The problem text is 1., which is not in a container, so is vertically aligned to the default `baseline`. If you can wrap that in another vertical-align:middle span, you should be golden... Oh... i adjusted the CSS to set a root `font-size` & using `rem` (relative em) for the font-size changes. Is this usable for your CSS? – cautionbug Dec 04 '14 at 21:16
  • I think the problem lies in the font choice. I tried changing the font to Arial, and it rendered the same in both browsers. Just a guess, but maybe serif and sans-serif get rendered differently by different browsers? – Ricky Goldman Dec 04 '14 at 21:18
  • Just for reference, i took an image of the rendered box: http://tinypic.com/r/jh8mk1/8 The 1. is what's not vertical-aligned. The spans are being vertical-aligned, (vertical-align probably isn't necessary for the container). – cautionbug Dec 04 '14 at 21:46
  • The letter is "close" to vertically centered, but it's a bit too high in Firefox and a bit too low in Chrome. Adjusting the font size makes this more apparent. It has nothing to do with font-family choice, as in the non-demo code I'm using an entirely different font. – brentonstrine Dec 04 '14 at 22:44
  • @cautionbug, I'm guessing that due to your browser/monitor/resolution you're not seeing the issue with the "A" as clearly. But if you increase/decrease the font, and compare between webkit & gecko, you should see what I'm talking about. Using `rem` units doesn't fix it. – brentonstrine Dec 04 '14 at 23:02

1 Answers1

2

On both the container and b block the line-height and font-size cause the height of the block to change. line-height and font-size interact with each other! line-height is the vertical distance between the lines of text in a container. So with line-height:60px and font-size:2.5em the height of the container grows to 73px. This is countered in CSS by your hard height:60px on the container but what this does not do is affect the text baseline. The text "1." in the container will move up or down if the baseline changes. If the font-size on the .a is increased the baseline drops, dragging the text "1." down. You can check this by turning all all vertical alignment and increasing the font-size of .a and the "1." content moves down. With this in mind, we should really only need to fix the container's vertical alignment, like this:

.container {
  display: block;
  overflow: hidden;
  height: 60px;
  line-height: 60px;
  vertical-align: top;
  font-size: 2.5em;
  width: 100%;
  text-align: center;
  background-color: lightgreen;
  text-decoration: none;
}
.a {
  font-size: 2em;
}
<a href="#" class="container">
  1.<span class="a">A.</span><span class="b">b b b b b b b</span>
</a>

But the baseline is still where the bottom of the A is since the A is the last container in normal flow, see:

baseline with A

And as long as the .b font-size is less than .a then the baseline will stay with .a, see next image with small font-size on .b:

baseline still with A

However, notice that the .b container has inherited the line-height:60px - see Why does this inline-block element have content that is not vertically aligned, so the height is correct, and we can vertical-align the .b to the top of the container which is the same height and therefore result in the text being middle aligned, like so:

.container {
  display: block;
  overflow: hidden;
  height: 60px;
  line-height: 60px;
  vertical-align: top;
  font-size: 2.5em;
  width: 100%;
  text-align: center;
  background-color: lightgreen;
  text-decoration: none;
}
.a {
  font-size: 2em;
}
.b {
  display: inline-block;
  font-size: 23px;
  vertical-align: top;
  font-family: sans-serif;
  text-align: center;
}
<a href="#" class="container">
1.<span class="a">A.</span><span class="b">b b b b b b b</span>
</a>
Community
  • 1
  • 1
andyb
  • 43,435
  • 12
  • 121
  • 150
  • Arg, using `em` resizes the `px`!! ... I didn't even think of that!! Thanks. – brentonstrine Dec 04 '14 at 23:07
  • In your first example, it looks like `.container` is the same--is the only difference that you removed `line-height:60px;` from `.a`? – brentonstrine Dec 04 '14 at 23:16
  • I removed `line-height` and `vertical-align` from `.a` and changed to `.container { vertical-align:top }` – andyb Dec 04 '14 at 23:22
  • I see, like [this](http://jsfiddle.net/tgv2rx7f/123/). Now that I'm realizing what the issue is, I see that Firefox and Chrome calculate where the baseline of a font is differently. In the example I just linked to here, the "A" is `60px` line height, height, and font-size. Yet Firefox places it higher up than Chrome!! – brentonstrine Dec 04 '14 at 23:50
  • Ok, check this out: http://jsfiddle.net/gys9neme/. In Firefox it's `60px` tall. In Chrome it's `69px`! – brentonstrine Dec 04 '14 at 23:55
  • And is `66px` for me locally (currently Windows 8.1, Chrome 41) – andyb Dec 05 '14 at 00:04
  • It's weird that Chrome doesn't actually show the extra pixels, but if you look at computed styles and go to `height`, it shows a value of `auto`, and gives line 15 as the source of that value. Of course line 15 is `height: 60px;`!! – brentonstrine Dec 05 '14 at 00:10
  • Don't forget in this new fiddle that the `height` will not apply since the `display` is `inline`. – andyb Dec 05 '14 at 00:21
  • Yes. I think at this point it's a separate issue (related to how browsers treat text line-heights). I'm going to accept your answer and create a new question on the topic. – brentonstrine Dec 05 '14 at 00:29
  • Here's the new question, FYI: [http://stackoverflow.com/questions/27306894/how-to-get-consistent-line-heights-between-browsers](http://stackoverflow.com/questions/27306894/how-to-get-consistent-line-heights-between-browsers) – brentonstrine Dec 05 '14 at 00:42