CSS Vertical Centering (without tables!)

There are a few different ways to vertically centre objects with CSS, but it can be difficult to choose the right one. I'll show you all the best ways I’ve seen and also how to create a nice little centred website.

Vertical centering with CSS isn’t an easy thing to do. There are many different ways that may not work in some browsers. When you work with CSS, it is important to know how it works, so let’s see 5 different ways of vertically centring objects, and what’s good and bad about them. (You can see my test page briefly explaining all of them.)

Note: I'm Australian, that’s why centre is spelt with ‘re’, not ‘er’. Don’t get confused when css'ing: centre must be spelt ‘center’

Method 1

This method sets some <div>s to display like a table, so we can use the table’s vertical-align property (which works very differently to other elements).

Note: You may be thinking “We weren't going to use tables”. The main problem with tables is that you should keep your markup semantic. If you code a table but aren't really using it as a table, it should be avoided (there's nothing wrong with using tables as real tables). The only thing wrong with telling a <div> to think it’s a table is it might create browser inconsistencies. This isn't a crazy idea — it’s juts like telling an <a> to display:block; or an <h2> to display:inline;.

The Goods

The content can dynamically change height (doesn't have to be defined in CSS)

Content doesn't get cut off when there isn't enough room in the wrapper

The Bads

Doesn't work in Internet Explorer (not even the IE 8 beta)

Lots of nested tags (not really that bad, this is a subjective topic)

Method 2

This method will use an absolutely positioned div, which has the top set to 50% and the top margin set to negative half the height of the content. This means the object must have a fixed height, that is defined by CSS.

Because it has a fixed height, you may want to set overflow:auto; to the content div, so if there is too much content to fit in, a scrollbar will appear, instead of the content continuing on outside the div!

The Goods

When there isn't enough space (ie. the window shrinks) our content will not be cut off, and a scrollbar will appear.

The Bads

Only one I can think of is that it requires an extra empty element (which isn't that bad, another subjective topic)

Method 4

This method uses a position:absolute; div with a fixed width and height. The div is then told to stretch to top:0; bottom:0;. It can't because of the fixed height, so margin:auto; will make it sit in the middle. This is similar to using the very common margin:0 auto; to horizontally centre block elements.

The Goods

The Bads

Content is cut off without scrollbar if there isn’t enough room in the container

Method 5

This method will only centre a single line of text. Simply set the line-height to the height of the object, and the text sits in the middle

<div id="content">
Content
</div>

#content {height:100px; line-height:100px;}

The Goods

Works in all browsers

Doesn't get cut off when the isn't enough space

The Bads

Only works on text (no block elements)

When there is more than a single line (like when it wraps), it breaks badly

This method is very useful on small elements, such as to centre the text inside a button or single-line text field.

Which Method?

My favourite method is number 3 — using a floater and clearing the content. It doesn't have any major downsides. Because the content will clear:both;, you can also put other elements above it, and when the windows collapses, the centred content will not cover them up. See the demo.

Before we can make our content vertically centred, the body and html must be stretched to 100% height. Because the height is inside the padding and margin, we have to make them 0 so a scrollbar doesn't appear just to show you a little margin.

The floater’s margin-bottom is half of the content’s height (400px), which is -200px.

You should now have something that looks like this:

The width of #centered is 80%. This is to make your site small on small screens and wide on bigger screens (on my medium-large screen, many old websites are small in the top left corner, and it is a little annoying). This is known as having a liquid layout. min-width and max-width are also set to stop it getting too big or too small. Internet Explorer doesn't support min & max width though — we will fix that later using the proprietary expression value. Obviously, you may choose to have a fixed width instead.

Because #centered is position:relative, we can use use absolute positioning inside it to position the elements. overflow:auto; was used on #content, so a scrollbar will appear when the content doesn’t fit inside it. Internet Explorer doesn't like overflow:auto; unless we tell it the height (not just top and bottom position, and not in %) so we did that as well.

Step 3

The last thing to do is add some more styles to make it look a bit nicer. Lets start with the menu.

The first thing to do when turning a list into a menu kind of thing is to remove the dot points with list-style:none and all the margin and padding. If you want it to have a margin or padding, make sure you specify exactly what, don't leave it to the web browsers defaults because they can vary.

The next thing to notice is that the links are set to display as a block element. This makes them fill the entire line and gives you more control over them. If you want to make your menu go horizontally (doesn't work in this design), then you can make them float as well.

The other interesting thing to notice about the menu is the :before and :after CSS pseudo-element let you insert content before and after elements. This is a good way to include little icons or characters such as the arrow at the end of each link. This doesn’t work in Internet Explorer before version 8 though.

Step 4

The last thing to do is add some more CSS to make the page look a bit nicer.

A thing to notice is the rounded corners on #centered. In CSS3, there should be a border-radius property to set the radius of rounded corners. This is not implemented by any major browsers yet, unless you use the -moz or -webkit prefixes (for Mozilla Firefox and Safari/Webkit)

Step 5 — Fixing IE

As you might have guessed, Internet Explorer is the only main browser which gives us trouble:

The #floater must have a width defined, or it doesn't do anything in any version of IE (already fixed)

IE 5 can't centre horizontally! (doesn’t like margin:0 auto;)

IE ≤ 6 has too much space around our menu, which breaks it

IE ≤ 6 doesn’t support min-width or max-width

To fix these problems, we are going to serve each version of IE its own extra stylesheet, using IE's proprietary conditional comments. They look like regular HTML comments (so other browsers ignore them completely) but IE will read the contents to decided if it should ignore them or not.

We set the condition inside the [ ] at the start. The lte means “less than or equal to” (≤). You can also use gte (≥), lt (<), gt (>), or omit it to just specify one version (eg, [if IE 6]). For a reference on more syntax, see Sitepoint's Reference.

So, to fix our problems, create 2 new stylesheets called “styles_ie5.css” and “styles_ie6.css”. Now, add to our html file the following code just after the original stylesheet, which links to the 2 new stylesheets:

Internet Explorer 5

The first stylesheet is for only IE 5, to fix the horizontal positioning. It uses a well-documented bug that means block elements follow the text-align property. We set the body to align center, and then our main #centered div back to align left, so we don’t have center-aligned text.

Internet Explorer 6, hasLayout & expression()

The IE6 stylesheet fixes a problem present in IE 5 as well, so its conditional comment condition is set to lte, so previous version of IE will see the link code as well.

To fix the problem of extra space around the menu is a bit more complicated. It has to do with IE’s internal hasLayout property. In Internet Explorer only some items “have layout” (such as html, body, table, fieldset), and these are the ones that control the layout of their children. All the elements that don’t have layout have less control over the display of the page, although they aren't totally ignored. Many of IE’s rendering problems can be fixed by giving elements “layout”. The simplest way to do this is to add the proprietary CSS property zoom to elements in an IE specific stylesheet.

The whole “hasLayout” thing can be very difficult to explain and understand. For a more detailed explanation, see Sitepoint’s Reference again.

The expression() function lets you use javascript statements within the CSS file to set dynamic values of objects (Internet Explorer only). The statement is re-evaluated often, so there is a performance decrease if you use them too much. This statment says “If the width of the window is greater than 1000px, set the width of #centered to 800px, otherwise, use 80%”