Vertical Centering of Inline Elements

I originally published this in June, 2006, as a simple demonstration of the results of a lot of head scratching at CSSCreator forums. I like to think I made a contribution, but it was Bruno Fasino who made the totally non-intuitive, logical leap that made everything actually work in IE≤7. Ingo Chao brought Bruno's work to those of us with slower wits, (OK, that was me; Chris..S caught on right away) and fashioned the practical model.

In this evolution, I try to make practical examples of the model as you would use it in real web pages.

It is a simple matter to center elements horizontally. All graphic browsers since IE6, ca. 2001 have supported {margin: auto;}, which centers the block element horizontally within its container. Likewise, the text-align property controls horizontal alignment of inline elements within their block containers.

It would be as trivial to center vertically if the majority browser—you know who you are, IE≤7—were to support the display property's table group of values. (For the purposes of this article, IE8 may be considered a modern browser.) This article deals with centering inline elements. Various articles in the Vertical Centering Section deal with other issues of vertical centering.

Fixed Height Containers

The original problem we were trying to find an answer to was, how do we make a vertical menu with equal height boxes, and vertically centered text of one, two, or three lines?

In this section, we'll demonstrate a simple block container with multiple centered lines of text, a container with an image and a caption, an example of creating more content than will fit the container, and finally, a menu.

But first, let's take a look at the way things work.

Centering Text in a Fixed Height Container

The Modern Browser

The key for modern browsers is the table-cell value for display. We make the parent container {display: table;} so we can set width, height, margins, &c., and we have a proper container for the inline contents. An element with {display: table-cell;} will fill its container if it's a table type.

So, this:

<style type="text/css">
p {
  border: 1px solid black;
  display: table;
  height: 200px;
  width: 200px;
  }

span {
  display: table-cell;
  vertical-align: middle;
  }
</style>

And, this:

  <p><span>line one
  <br />
  line two</span></p>

Yields this:

line one
line two

You may have noticed that IE doesn't seem to be cooperating. We'll discus that in the next section.

Legacy Browsers: IE6 & IE7

The leap that Bruno made was to create two inline elements that aligned with one another. I only confuse myself when I try to rationalize what's happening, but I'll try to explain it anyhow.

What we want to do is create an anonymous inline box the full height of the container. One way to do this is to make the inline contents {display: inline-block;}. In the following explanatory illustrations, the <span> is colored pink, and the <b> is colored yellow, to help you see their dimensions.

The html will have a conditional comment to feed IE an extra element.

  <p><span>line one
  <br />
  line two</span><!--[if lte ie 7]><b></b><![endif]--></p>
Fig. 1

No height is given either element. The line-box's height is determined by the tallest element, the span. Notice that the bold (exes added for clarity) is centered vertically against the span.

Fig. 2

We want the line-box to be the full height of the parent container, so this time both elements are given {height: 100%;}. We have full height, but remember, an inline-block element has an interior behavior like a block element. The text is at the top, as to be expected.

Fig. 3

Give just one element, the span, a height, and leave the other auto. That works as desired, except that the bold element is centered against the span; just the opposite of what we want.

Fig. 4

Set the bold to 100%, and the unsized span aligns itself to the middle of the line-box. Now that IE has a work-around, the colors and unneeded text can be removed.

Put it all Together

Removing all the extraneous stuff, we can selectively add the IE work-around to the basic method.

  <p><span>line one
  <br />
  line two</span> <!--[if lte ie 7]><b></b><![endif]--></p>
<style type="text/css">
p {
  border: 1px solid black;
  display: table;
  height: 200px;
  width: 200px;
  }

span {
  display: table-cell;
  vertical-align; middle;
  }
</style>

<!--[if lte ie 7]>
<style type="text/css">

span {
  display: inline-block;
  }

b {
  display: inline-block;
  height: 100%;
  vertical-align: middle;
  }

</style>
<![endif]-->

Practical Examples

Read the source for the specifics of each example.

Simple Text and Images

Centering multiple lines
in a block container.


An image and a caption

Handling Overflow

This example shows the dangers of depending on buggy behavior to provide the desired result. Modern browsers properly expand the container, because it's displaying as a table, and that's the way tables operate. IE≤6 expands the container because an element with hasLayout expands even when it's not supposed to. Since IE uses inline-block instead of table, it shouldn't expand. IE7 has fixed this bug. The result is that IE6's bug gives the desired rendering while IE7 doesn't.

Take care that you allow sufficient height or less content to avoid this problem.


And a caption
that just
goes on,
and on,
and on,
and on some more

A Vertical Menu with Equal Sized Boxes

Notice that the list-items have a fixed height. Table displayed elements shrink-wrap their contents. For modern browsers, overflow will cause the element to expand, which is probably alright. IE6 will also expand due to having layout. IE7 will over flow. Overflow is created in the fourth list item.

Vertical Centering in Dynamic Height Containers

That's a bit misleading. All elements displayed as one of the table group are effectively of dynamic size. Any declared height or width should be considered a minimum, as a table must enclose its content.

A Horizontal Menu with Equal sized Boxes

The horizontal menu differs from the vertical in that the ul is now the table container and the list items are displayed as table cells. The advantage, in modern browsers, is that each cell will be the same height as the others, just as a row of cells in a table are all the same height.

Issues with IE

We must still treat IE to a fixed height. If we don't, each box will seek its own height, which is what we don't want. If we do set a height, we face the overflow issue again.

An Example

In this demo, the same markup is used as in the vertical menu; only the style rules are changed. Box height for IE is the same as above, 4em, enough for three lines.

Summary

CSS2.1 provides an elegant solution to the problem of centering one element within another. IE, being so far behind the curve on CSS support, requires heroic measures to do what other browsers do so simply. For now, it is probably a sane approach to simply use flat tables for all but the vertical menu—no matter how poorly structured and non-semantic your markup must be.

If you're careful about the possible overflow issues in IE, Bruno's sweet little hack is easy to apply, if not to grok in fullness.