hasLayout.net

Horizontal Centering

Description

Virtually every method of horizontal centering with CSS

Date of the Tutorial

Fri Aug 14 06:22:21 2009

Overview

In Stone Age people used <center> element and align attribute to center elements on their pages... never use those. With the arrival of CSS the old methods have been deprecated as CSS is able to offer more flexibility and easier maintenance with regards to centering.

What is important to understand is that with CSS you don't just push the magic button and hope everything will work out for anything you want to center. You need to know what you want to center and what styles have been applied to that element in order to choose an appropriate method for centering.

In this tutorial I provide examples which demonstrate a particular centering method. Sometimes the effect may differ in Internet Explorer and standards compliant browsers. If you do not have Internet Explorer installed on your system you may want to use freely available tools such as NetRenderer or Total Validator.

Block-level and Inline-level Content

First step in centering with CSS is to find out what kind of element you are trying to center. Is it block-level or inline-level?

Different methods are used for each type of content. Plain text, images, ' element"><span>, etc. are inline-level content, while elements such as ' element"><div> and ' element"><h1> are block-level content.

Using the display property, authors can change the way the element is displayed.

Which elements are block-level and which are inline-level is beyond the scope of this document. There plenty of online resources available that describe everything in detail and I am not going to reinvent the wheel. Wikipedia has a list of inline-level elements as well as a list of block-level elements.

Centering Inline-level Content

Centering of inline-level content is generally done using text-align property. Even though it has word text in it, it does not center text only. Yes, poor naming.

Example 1

Lorem Ipsum!
Lorem Ipsum

HTML Code
<p>
        Lorem Ipsum!<br>
        <img src="smiley.png" alt="Lorem Ipsum" width="35" height="35">
</p>
CSS code
p { text-align: center; }

Note that we have used text-align: center on the parent block element, not on the elements we want to center.

Two things that you should keep in mind when using text-align:

  • text-align is inherited meaning that if you use body { text-align: center; } and have <h1>Foobar<h1>, your ' element"><h1> will have centered text as well, same way as if you would use h1 { text-align: center; }. To avoid this, simply set text-align back to the original value (left for Western, left-to-righ typography) on the descendants, possibly using a universal selector.

Centering Block-level Content with Known Width

Centering block-level elements takes on a different approach. As opposed to centering of inline-level content we will be applying styles to the element we wish to center. Centering of block-level elements is done using the margin property with value auto. According to CSS specification section on computing widths and margins, when left and right margins are set to value auto they will be equal.

A common mistake newcomers make is expecting an element to be centered simply by setting margin: 0 auto;. Block-level elements in general occupy all of the available to them width if width is set to it's default value auto. Technically, the element with margin: 0 auto; width: auto; is centered, but since it occupies all of the available width the effect is not visible.

To center block-level content in addition to setting left and right margins to value auto you must also give it some width with width property. Exception to this rule are elements with intrinsic dimentions, for example images, where width is already known. Another exception are elements with table display types.

Example 2

Lorem Ipsum!
Lorem Ipsum

Lorem Ipsum!
Lorem Ipsum

Lorem Ipsum!
Lorem Ipsum

HTML Code
<div id="container">
        <p id="widthless">
                Lorem Ipsum!<br>
                <img src="smiley.png" alt="Lorem Ipsum"  width="35" height="35">
        </p>
        <p id="inherited">
                Lorem Ipsum!<br>
                <img src="smiley.png" alt="Lorem Ipsum"  width="35" height="35">
        </p>
        <p id="override">
                Lorem Ipsum!<br>
                <img src="smiley.png" alt="Lorem Ipsum"  width="35" height="35">
        </p>
</div>
CSS code
p { margin: 1em; }
#container { text-align: center; }
        #widthless { margin: 0 auto;}
        #inherited
        {
                width: 50%;
                margin-left: auto;
                margin-right: auto;
        }
        #override
        {
                text-align: left;
                width: 50%;
                margin: 0 auto;
        }

The example shows a few things that I would like you to know. Let's break this one down. First of all, note the p { margin: 1em; } as the first line in CSS code. It sets 1em margins to all the paragraphs. It's not relevant to centering, just remember that we have it there.

Second of all, #container { text-align: center; } is there to demonstrate text-align property's inheritance, which I mentioned in centering inline-level content section. As you can see #widthless and #inherited have centered text and the image even though text-align: center is not directly applied to them. On #override I have used text-align: left; to restore the effect of text-align property.

Now back to block-level content. As I've said, block-level element with width: auto, which is what you get if you don't specify width, expands to fill the entire width of its parent. You can see the effect on #widthless.

Note the difference between #inherited { margin-left: auto; margin-right: auto; } and #override { margin: 0 auto; }. They are both centered, however #override has top and bottom margins zeroed out, whereas #inherited have them set to 1em (I told you to remember the first line of CSS code). Reason for that effect is the shorthand margin that, in our example, sets top and bottom margins to 0 and left and right margins to auto. Both expanded and shorthand versions work fine with regards to centering.

Centering Block-level Content With Unknown Width

Everything is straight-forward until you want to center block-level element with dynamic width, which in practice happens often enough. For standards compliant browsers everything stays straigh-forward with the use of certain values for display property... that IE versions below 8 does not support. Some hackery is needed to make those IEs surrender, however it's not perfect and does have cases where centering will not work.

I will show you a relatively large example to demonstrate the workings and the issues. Please don't run away screaming "It's ugly" but understand what each line of code does and what it is for. After all, the method uses only two lines of code for sane browsers and three lines of code for IE.

Example 3

Lorem Ipsum!
Lorem Ipsum


Lorem Ipsum! Lorem Ipsum! Lorem Ipsum!
Lorem Ipsum


Lorem Ipsum! Lorem Ipsum! Lorem Ipsum!
Lorem Ipsum

borker


Lorem Ipsum! Lorem Ipsum! Lorem Ipsum!
Lorem Ipsum

borker

HTML Code
<div id="container">
        <p class="inner">
                Lorem Ipsum!<br>
                <img src="smiley.png" alt="Lorem Ipsum"  width="35" height="35">
        </p>
        <br>
        <p class="inner">
                Lorem Ipsum! Lorem Ipsum! Lorem Ipsum!<br>
                <img src="smiley.png" alt="Lorem Ipsum"  width="35" height="35">
        </p>
        <br>
        <div class="inner">
                Lorem Ipsum! Lorem Ipsum! Lorem Ipsum!<br>
                <img src="smiley.png" alt="Lorem Ipsum"  width="35" height="35">
                <p id="borker1">borker</p>
        </div>
        <br>
        <div class="inner">
                Lorem Ipsum! Lorem Ipsum! Lorem Ipsum!<br>
                <img src="smiley.png" alt="Lorem Ipsum"  width="35" height="35">
                <p id="borker2">borker</p>
        </div>
</div>
CSS code
.inner
{
        display: table;
        margin: 10px auto;
}
#container > br { display: none; }
        #borker1
        {
                width: 5em; /* gives "layout"
                        and sets width smaller than the container */
                background: #ddd;
        }
        #borker2
        {
                height: 2em; /* gives "layout" but width is auto */
                background: #ddd;
        }
condcom code
<!--[if lt IE 8]>
        <style type="text/css">
                #container { text-align: center; }
                        #container br { display: block; }
                        #container * { text-align: left; }
                        .inner
                        {
                                zoom: 1;
                                display: inline;
                        }
        </style>
<![endif]-->

Let's break this one down. First of all let's see what makes sane browsers tick, then we will go over IE code inside conditional comments.

You may have noticed .inner { margin: 10px auto; } and it rang the bell from our block with known width center section. It sets left and right margins to value auto, which as we know already centers the element. However, we did not set any width, we didn't because we don't know it. What we did instead is used the display property with value table. It causes "shrink-wrapping" of our element, same as float does, which is exactly what we needed.

Now the yummy part - IE tricks. First of all, none of the IE versions support display: table. In fact, Microsoft took the no tables approach so seriously that they decided that all of the table* display values are bad as well. That's how we got where we are.

Anyway, first things first, why all those ' element"><br>s. The reason for them is that in IE below version 8 we are "shrink-wrapping" with display: inline and that causes all of our elements to stack on one line, ' element"><br> prevents that. Also, ' element"><br> causes some extra space in sane browsers. Since we need it only for affected IEs, it's safe enough to hide it with display: none, which we are reverting to display: block inside conditional comments.

With different "shrink-wrap" method goes a different centering method. Recall the centering inline-level content section. Since our .inner with display: inline acts like inline-level content we will center them with text-align: center on the parent container (#container in our example). Rule #container * { text-align: left; } cancels the inheritance, thus the content inside .inner is not centered.

Some of you may wonder what .inner { zoom: 1; } does. It's one of many ways to give an element "layout", which means setting IE's hasLayout to true. If we would not have it on .inner we would get some bugs regarding padding and inner elements containment, take it out if you are interested in seeing the effect.

As I have said, this method is not perfect. Take a look at the last two boxes, the ones that have #borker1 and #borker2 inside. As you can see, the last .inner is not shrink wrapped in IE version below 7 at all. Reason for that is that height gives "layout", so does width which you see in the #borker1 (second last box in the example). It seems that #borker1 does not break the shrink wrap as #borker2 does. In a nutshell, if the element inside our shrink wrapped friends has "layout" and the width is not restrained our shrink wrap will fail. It's a rare case, however not an impossible one. If you come about something like this, your options are limited if you are supporting IE versions below 7. One of the workarounds may include using an extra element without width to simulate full width (e.g. background) and setting restraining the width of the element that causes the problem.

Centering Floated Elements

First I would like to note that if you are attempting to center an element which is floated you are probably going in the wrong direction already and you want either set display: inline and center with inline-level centering or you are after unknown width centering.

Centering of floated elements involves the use of position property with value relative and the use of left or right properties.

Example 4

Lorem Ipsum!
Lorem Ipsum

Lorem Ipsum!
Lorem Ipsum

HTML Code
<div id="container">
        <div id="inner1">
                <p>
                        Lorem Ipsum!<br>
                        <img src="smiley.png" alt="Lorem Ipsum"  width="35" height="35">
                </p>
        </div>
        <div id="inner2">
                <p>
                        Lorem Ipsum!<br>
                        <img src="smiley.png" alt="Lorem Ipsum"  width="35" height="35">
                </p>
        </div>
</div>
CSS code
#container { overflow: hidden; }
        #inner1, #inner2
        {
                position: relative;
                left: 50%;
                float: left;
        }
        #inner2
        {
                clear: both;
                overflow: hidden;
        }
                #container p
                {
                        position: relative;
                        right: 50%;
                }
condcom code
<!--[if lt IE 8]>
        <style type="text/css">
                #container
                {
                        zoom: 1;
                        position: relative;
                }
                        #container p { float: left; }
        </style>
<![endif]-->

Alright, let's go over the code. For those of you who are already yelling "Second element is cut off half way" I suggest to hold the horses and understand how the centering process works. First off, let's get everything that is not directly relevant to centering out of the way, except for code in conditional comments which I will cover last.

The <div id="container"> is there to demonstrate the IE issue (or rather how to fix it) since in real world you would usually have centered floats inside some containing element. Code #container { overflow: hidden; } contains floated elements. To contain floats in IE versions below 8 all the element needs is "layout" which is what #container { zoom: 1; } inside conditional comments does. Don't think that #inner2 { overflow: hidden; } is there to contain floats. It is exactly what causes the second element in the example to be cut off. I'll explain why in a second, just keep that in mind when using this method of centering.

Now the actual centering bit. The left and right properties when used with position: relative offset the element into the corresponding direction, same as margins do, but with the difference that the containing element "thinks" that the element was not offset at all. If you refer to our cut off element you can picture that the element was moved half the parent's width (with right: 50%) but the parent doesn't know (or care?) about that, thus overflow: hidden; cuts off the part of the element that is sticking out of the parent.

Code #inner1, #inner2 { position: relative; left: 50%; float: left; } "shrink-wraps" the element and offsets it in such a way, that the left edge of the element is in the center of the parent. We are half way there, what we need now is to offset it in such a way that not the left edge but the middle of the element would be in the center of the parent element.

I am not a math professor, but something tells me that we can accomplish that by offsetting the <p> inside #inner1 half the width of #inner. It is exactly what we are doing with #container p { position: relative; right: 50%; }. The difference lies in the fact that percents in #inner1 { left: 50% } were referring to the width of the #container but percents in #container p { right: 50%; } refer to the width of our shrink-wrapped #inner. If it would make it easier for you to understand right: 50%; can be rewritten as left: -50% (note the minus sign).

Centering of Absolutely Positioned Elements With Known Width

After all that hackery above, let's take a break and look at the way to center absolutely positioned elements (ones with position: absolute set on them) for which the width is known.

Example 5

Lorem Ipsum!
Lorem Ipsum

HTML Code
<div>
        <p>
                Lorem Ipsum!<br>
                <img src="smiley.png" alt="Lorem Ipsum"  width="35" height="35">
        </p>
</div>
CSS code
div
{
        height: 130px;
        position: relative;
}
        div p
        {
                height: 120px;
                position: absolute;
                top: 0;
                left: 50%;
                width: 8em;
                margin-left: -4em; /* half the width */
        }

Everything is straight-forward. Code div { position: relative; } establishes the containing block for our absolutely positioned element. Meaning that top and left properties will offset our <p> relative to the <div>

The ' element"><p> has width set to 8 em units. Code div p { left: 50%; } puts the left edge of the ' element"><p> into the center of the ' element"><div>. That doesn't exactly center the ' element"><p> which leads us to the second bit - div p { margin-left: -4em }. Note the minus sign and note that the margin is set to half of the width which moves the center of ' element"><p> into the center of the ' element"><div>. Exactly what we needed.

A note on why I have used div p { top: 0; }. It is not relevant to centering at all. However, Internet Explorer often likes to misplace absolutely positioned elements unless both top(bottom) and right(left) properties are set, i.e. both x and y positions are known.

Centering Absolutely Positioned Elements With Unknown Width

Time to put your seatbelts back on, IE's expanding shrink wrap bug is here to hunt us again.

Example 6

LoremIpsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum Lorem Ipsum
Lorem Ipsum

Lorem Ipsum! Lorem Ipsum!
Lorem Ipsum

HTML Code
<div id="container">
        <div class="outer">
                <div class="inner">
                        <p>
                                LoremIpsum Lorem Ipsum
                                Lorem Ipsum Lorem Ipsum
                                Lorem Ipsum Lorem Ipsum
                                Lorem Ipsum Lorem Ipsum
                                Lorem Ipsum Lorem Ipsum
                                Lorem Ipsum Lorem Ipsum
                                Lorem Ipsum Lorem Ipsum
                                Lorem Ipsum<br>
                                <img src="smiley.png"
                                        alt="Lorem Ipsum"  width="35" height="35">
                        </p>
                </div>
        </div>
        <div class="outer2">
                <div class="inner">
                        <p>
                                Lorem Ipsum!
                                Lorem Ipsum!<br>
                                <img src="smiley.png"
                                        alt="Lorem Ipsum"  width="35" height="35">
                        </p>
                </div>
        </div>
</div>
CSS code
#container
{
        height: 200px;
        position: relative;
        overflow: hidden;
}
        .outer, .outer2
        {
                position: absolute;
                top: 0;
                width: 200%;
                left: -50%;
        }
        .outer2 { top: 85px; }
                .inner
                {
                        position: absolute;
                        left: 50%;
                }
                        .inner p
                        {
                                position: relative;
                                left: -50%;
                        }
                        .outer2 p
                        {
                                background: #afa;
                        }
condcom code
<!--[if lt IE 8]>
        <style type="text/css">
                .inner p { float: left; }
        </style>
<![endif]-->

The method is extremely similar to method of centering floats. However, there may seem to be some redundant or unexpected code, .outer, .outer2 { width: 200%; } in particular.

First of all it's important to understand the difference on how left and right properties affect absolutely and relatively positioned elements.

When position property is set to value relative, code left: 50% will offset the entire element by 50% of the width of the parent container. However, when the position property is set to absolute and width is set to auto (the default width value), left: 50% will move the left margin edge of the element by 50% of the width of the container, i.e. resizing the element.

So what problem does it create to us? If we would to try to center our absolutely positioned element the same way we centered floats subsequently changing position: relative to position: absolute, our "shrink-wrapped" element would be able to expand only up to the half of its parent. If that's good enough for you, you can avoid the second wrapper ' element"><div> in the example above.

With obstacles addressed, let's begin breaking down the code and looking at what it does. Once again, #container { position: relative; } establishes containing block for our absolutely positioned elements. However, note the #container { overflow: hidden; } it not there to contain any floats as one may think at first. It does exactly that, hides any overflow. Where is that overflow coming from? Take a guess, it's from .outer, .outer2 { width: 200%; } that makes .outer and .outer2 twice as wide as the parent, but #container { overflow: hidden; } hides that extra half of width.

Now, let's follow the "tree" of our left property values on all those container and see each step's purpose.

Along with doubled width, our .outer also has left: -50%; (note the minus sign). Since we did define width, left moves the entire element to the right (because we have used a negative value for left), half the parent's width. Now the horizontal middle of .outer is in the center of #container with 25% of .outer's width (remember, we doubled the width) sticking out of the #container on each side. If you don't understand why 25% on each side, let me follow again on our dimensions. .outer's width is double the parent's width, left: -50% moved .outer half of the parent's width which is exactly 25% of .outer's width. Since originally .outer's left edge was at the left edge of the #container and we moved it 25% of .outer's width the other 25% of .outer's width will end up sticking out on the other side of #container. What we basically did is create a centered container that is double the width of the #container and if you were following the above paragraphs you know why we wanted this - our shrink wrapped element will expand only half the width of that parent, but since we have just doubled the width of the parent, the shrink wrapped element will be able to expand up to 100% of the #container.

Next step is absolutely positioned .inner with left: 50% on it (note that this time it is a positive value). Since .inner does not have any width set (i.e. has width: auto since that's the default), left: 50% moves the left margin edge of .inner to the center of .outer consequently allowing the shrink-wrapped contents of .inner to expand only up to the half of .outer's width, but since we have doubled .outer's width, this effect is exactly what we want.

Final step is the actual element that we were trying to center in the first place, is our .inner p. Note that it is relatively positioned (i.e. has position: relative) and the reason is that we want left: -50% (negative value again) to offset the element itself instead of only its margin edge. Now .inner's left margin edge is in the center of .outer (and of course the center of #container) as well as .inner is shrink wrapped around our ' element"><p>, thus ' element"><p>'s left margin edge is in the center of #container. Offsetting ' element"><p> by half the .inner's width is the same thing as offsetting it by the half of its own width that will perfectly center it inside #container, which is what we wanted.

Special Note On Images and Content With Intrinsic Dimensions

I often see people being hard on themselves, trying to center some design related image using ' element"><img> element. First of all, you should use ' element"><img> element only for content level imagery such as photographs, charts and maps, basically images which would not be changed if the website would be redesigned. For presentation level imagery such as borders, pretty design elements, rounded thingies etc. you should use the background property.

However, if your image is content level, and you are trying to center it, there is a trick. Images have intrinsic dimensions, meaning that browser knows image's height and width without you explicitly telling about it. Referring back to our centering block-level content with known width section we know that margin: 0 auto will center an element providing you have set width. Since elements with intrinsic dimensions already have that width, specifying just margin: 0 auto will successfully center them.

"Wait a second", one may say, "images are inline-level content, margin: 0 auto will not center them". That's where the display property joins the game. Setting display: block on our ' element"><img> element makes it act like a block-level element.

Example 7

Your browser must render images and support CSS in order to view this example.

Lorem Ipsum

Lorem Ipsum design
HTML Code
<div>
        <p>Lorem Ipsum</p>
        Lorem Ipsum
        <img src="design.jpg" width="100" height="80" alt="design">
</div>
CSS code
div { text-align: left; }
        p
        {
                width: 80%;
                margin: 30px auto;
                background: #fff url(design.jpg) no-repeat center;
                color: #000;
        }
        img
        {
                display: block;
                margin: 0 auto;
        }

The example shows two methods that I've just mentioned. First, note that I've set div { text-align: left; }. I did it only to assure you that we have not set text-align: center anywhere since that's not how we are doing the centering in this example.

Take a close look at our ' element"><p>. The image is there. The image is centered. However, it is cut off. Yes, setting background does not affect the size of the element in any way, neither can you stretch or set dimensions on background images. The actual centering is done by the center word in the value of background property, which is a shorthand. The effect would be the same if we would have used background-position: center center. The values that background-position property accepts is beyond the scope of this tutorial.

Now let's take a look at our ' element"><img> element and the text that is directly inside the ' element"><div>. I want to note that having plain text inside the ' element"><div> is not semantic, however I did not want to make my example too complicated.

The ' element"><img> is centered, text is not. What's the magic? It's right there, img { display: block; margin: 0 auto; }. As I've mentioned earlier, img { display: block; } makes our image act like a block-level element. Since ' element"><img> is an element with intrinsic dimensions, setting img { margin: 0 auto; } centers it, despite the fact that we haven't set width on the image.

Internet Explorer Bugs

There are two modes in IE: standards compliant mode (buggy) and quirks mode (super buggy). The modes are switched with the DOCTYPE which is known as Doctype Switching.

When Internet Explorer is in quirks mode, it does not center block-level elements with margin: 0 auto. So how would you center it? First of all, you should not have your IE in quirks mode. I understand that under some circumstances changing the DOCTYPE could be impossible. However, there is another bug in Internet Explorer, which can be used to center block-level elements in quirks mode. Let's take a look at the example and I will explain what is going on.

Example 8

I am not affected

Lorem Ipsum!
Lorem Ipsum

HTML Code
<div>
        <p id="fixed">I am not affected</p>
        <p id="broken">
                Lorem Ipsum!<br>
                <img src="smiley.png" alt="Lorem Ipsum"
                                width="35" height="35">
        </p>
</div>
CSS code
div { text-align: center; }
        p
        {
                margin: 1%;
                width: 20%;
        }
        #fixed { float: left; }
        #broken { clear: left; }

In the example there are two paragraphs, one is #fixed another one is #broken. The ' element"><div> has text-align: center applied to it which, in standards compliant browsers does not center our block-level ' element"><p>s. Take a look in Internet Explorer however. What is going on? Apparently #broken is centered with respect to the ' element"><div> it is contained in. But what's the difference between #fixed and #broken? The float.

As I have mentioned before, the expanding box model bug messes up virtually every centering method I have covered. It is out of scope of this tutorial, and I haven't found any usable solution for it. I will do more research on it, and hopefully will write a tutorial on how to fix it which will be hosted on hasLayout.net.

Sources

  • David Dorward - Centering using CSS
  • PMOB.co.uk - Centering widthless floats
  • CSS Specification

Further Reading

  • margin property
  • display property
  • position property
  • top, right, bottom and left properties
  • float property
  • text-align property