Many designs call for centering content, either vertically or horizontally. It’s pretty straightforward to center horizontally, but if you wish to support Internet Explorer 7 and earlier, it takes a bit more work to center vertically.
Other than older versions of IE, this page only targets the recent versions of Safari (4.0), Firefox (3.6), and Opera (10). Browsers which use the same engines as these (e.g. Chrome uses Safari’s Webkit) should behave similarly.
Centering text
Centering text in a container (such as a paragraph) is very straightforward.
The HTML for this block is:
<div>This text is centered.</div>
and the necessary CSS is:
div { text-align: center; }
Note the actual CSS on this page includes a more complicated selector to target only this one <div>
.
The text-align
property applies to a block element and centers all of the content in that block. If you show the background by enabling the checkbox, you’ll see that the block spans the width of its container, and centering the text within the block also centers the text within the outer container.
Centering a block
The previous example shows how to center text. Since text is an inline element, then its position is affected by the setting of text-align
on the containing block. However, that attribute doesn’t apply to block elements.
If you wish to center a block in another block, you can set the left and right margins to auto
. The HTML for this example is the same as the previous one except for the actual text. The CSS is:
div { width: 50%; margin-left: auto; margin-right: auto; }
Enabling the background shows that the block is indeed centered within the container. The text itself, however, seems to be skewed slightly to the left, because it does not completely fill the width of the div
.
Floated blocks
You cannot set margins to auto to center a floated block, as seen here.
The only change to the CSS is to make the block float instead of setting the width:
div { float: left; margin-left: auto; margin-right: auto; }
This behavior follows the definition:
If ‘margin-left’, or ‘margin-right’ are computed as ‘auto’, their used value is ‘0’.
If you show the background, you’ll see that the block did indeed collapse in width, so while it ends up not being the full width of the container, the auto
for the margins is interpreted as 0
.
Inline blocks
Even though they are blocks, objects set to display as inline-block
(such as images) are treated as inline objects when computing the margins.
The block used in this example is a <span>
, since older versions of IE can only set inline-block
on objects which are normally inline
. Again, the CSS is similar to the above examples:
span { display: inline-block; margin-left: auto; margin-right: auto; }
As with floats, the definition says:
A computed value of ‘auto’ for ‘margin-left’ or ‘margin-right’ becomes a used value of ‘0’.
Why isn’t my block centered?
If you’re having problems centering a block by setting the margins, make sure the block isn’t spanning the full width of its container (which is the default).
As shown above, floated blocks cannot use auto
for the margins.
Another thing to check is that you’re trying to set the margins on a block element, not an inline element (such as images or other objects explicitly set to inline-block
).
Vertical centering
The easiest way to center objects vertically is to do it in a table cell. In fact, the CSS definition says vertical-align
Applies to: inline-level and ‘table-cell’ elements
However, there are many times when other tags make more sense semantically. One way around that is to use a <div>
or other block elements and set it to display as table-cell
. In order for that to work, you need to have it embedded in a block which has its display set to table
.
The table
block has a yellow background and the tcell
block has a cyan background. Since there is no padding on the table
block, none of the yellow background shows.
The HTML for this code is:
<div class="table"> <div class="tcell"> This text should be centered vertically for recent browsers. </div> </div>
The CSS is:
.table { display: table; height: 5em; } .tcell { display: table-cell; vertical-align: middle; }
While it may be strange to put a table cell directly into a table without a table row, CSS allows for the automatic insertion of anonymous table objects:
If the parent P of a ‘table-cell’ box T is not a ‘table-row’, a box corresponding to a ‘table-row’ will be generated between P and T. This box will span all consecutive ‘table-cell’ sibling boxes of T.
IE 7 and earlier
The markup and CSS for vertical centering are very straightforward, but since IE 7 and earlier don’t support these display types, another method needs to be used:
The HTML requires one more wrapper block than the previous example:
<div class="outer"> <div class="middle"> <div class="inner"> This text should be centered vertically for IE 7 and earlier. </div> </div> </div>
and the CSS:
.outer { position: relative; height: 5em; } .middle { position: absolute; top: 50%; } .inner { position: relative; top: -50%; }
If you’re on a browser other than IE 7 or earlier, the image at the right shows a piece of the example with the background enabled, and it shows how positioning is used. The yellow represents
outer
, the cyan middle
, and the magenta inner
.
The outer
box has a set height, and has position: relative
because we want to position the middle
box relative to it.
The middle
box is partially obscured, but it’s actually the same height as the magenta box. As you can see, the top of the cyan box would be half way down the yellow box.
The top of the inner
box is then moved up 50% the height of the cyan box, which then centers the content around the top of the cyan box, and thus centers the content in the yellow box. The reason inner
is not set to position: absolute
is that would cause middle
to have no content (since inner
would be removed from the content flow) and inner
would be moved up 50% of 0, meaning not at all.
IE 7 example on current browsers
If you’re looking at this example in IE 8, Firefox, or Opera, you won’t see the cyan box, because inner
does not get moved. In Safari, the top of the cyan box is in the vertical center of the yellow box and the magenta box is at the top.
At first blush, it’s not clear why this method doesn’t work for all browsers. Since the position of the magenta box depends on the height of the cyan box, but the height of the cyan box depends on the height of the magenta box, it would seem logical that the magenta box layout should be done first, then the cyan box, then the magenta box repositioned. However, when computing the width of the cyan box, CSS3 says that using a percentage for the width of a box:
Specifies a percentage width. The percentage is calculated with respect to the width of the generated box’s containing block. If the containing block’s width depends on this element’s width, then the resulting layout is undefined in CSS3.
Since this statement is part of the definition of both width
and height
, presumably it also applies to height. It seems the other browsers are assuming that undefined means 0 and the top is moved up half of that, which is why it stays in place. Or, it could be that 50% of an undefined value is undefined, so the box doesn’t move. Either way, the result is the same.
As for Safari, it seems if the height of the containing box is undefined, it uses the height of the next container which has a defined height, namely outer
which has a height of 5em
. Technically this doesn’t seem incorrect, but it’s not the expected behavior.
Looking at the CSS2.1 spec, specifying a percentage for height says that it:
Specifies a percentage height. The percentage is calculated with respect to the height of the generated box’s containing block. If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned, the value computes to ‘auto’.
The difference from CSS3 is the height is auto
rather than undefined. Again, it seems up to interpretation what the actual value should be.
Note that setting an explicit height on middle
would cause the magenta box to be positioned appropriately (since it would be clear that 50% of 5em is 2.5em), as would setting an explicit position for the top of the magenta box (since it wouldn’t depend on the height of the container). However, we wouldn’t want to do either, since we don’t know how tall the content would be for the magenta box (and therefore the cyan box).
Vertical centering for both older and current browsers
We can combine the two techniques to vertically center an object both IE 7 (and earlier) and the other current browsers.
The HTML is:
<div class="table"> <div class="tcell"> <div class="inner"> This text should be centered vertically for IE 7 and earlier, as well as current browsers. </div> </div> </div>
The CSS is:
.table { display: table; position: relative; height: 5em; } .tcell { display: table-cell; position: absolute; top: 50%; vertical-align: middle; } .inner { position: relative; top: -50%; } .table div { position: inherit; top: inherit; }
If you show the backgrounds, table
is yellow, tcell
is cyan, and inner
is magenta.
At first glance this seems to be an unceremonious mash-up, and in a way it is. We know that IE 7 and earlier don’t understand display: table-cell
, and it doesn’t matter if table
has display: table
for those browsers. Since vertical-align
doesn’t apply to a plain <div>
, IE 7 will ignore that, too.
However, IE 7 also doesn’t understand using display: inherit
, so it ignores that particular value for the tcell
and inner
blocks. With that, IE 7 shows the magenta block in the vertical center of the yellow box.
With just the inherited position, Safari, Firefox, and Opera all show the magenta cell in the vertical center of the yellow box, but IE 8 does not. In fact, with only that override IE 8 would position the cyan cell half way down the yellow box, and the position the magenta box in the middle of the cyan box (presumably because the vertical-align
takes precedence over the positioning).
While IE 8’s behavior seems to make sense, the other browsers actually have it right. The CSS definition says of position: relative
:
The effect of ‘position:relative’ on table-row-group, table-header-group, table-footer-group, table-row, table-column-group, table-column, table-cell, and table-caption elements is undefined.
That means that tcell
should not be pushed down. However, inner
is just a normal block, so it seems that it should be moved up because of top: -50%
. Even though it and tcell
inherit position: relative
, Safari uses the value of position: static
for inner
. It probably does that since tcell
actually has an undefined value for position
.
Firefox and Opera both claim to use position: relative
for inner
and even have a negative value for top
, but they also show the magenta box in the middle. It could be that, like IE 8, vertical-position
takes precedence.
To fix IE 8, we also add top: inherit
so both tcell
and inner
inherit the top position of table
(which defaults to 0
). This also makes the positioning intent more explicit for Firefox and Opera (Safari will ignore it since it’s using static positioning). Just as with position
, IE 7 and earlier do not know how to inherit top
, so the end result is the magenta box is centered for all the targeted browsers.
Hey there, I think your site might be having browser compatibility issues.
When I look at your blog in Firefox, it looks fine but when opening
in Internet Explorer, it has some overlapping.
I just wanted to give you a quick heads up!
Other then that, excellent blog!
Thanks for the note. I haven’t tested against IE for a while. I’m using a pretty old template, so I suppose I’ll end up using a newer one which should fix the problem.