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;.

<div id="wrapper">
	<div id="cell">
		<div class="content">
			Content goes here
		</div>
	</div>
</div>
#wrapper {display:table;}
#cell {display:table-cell; vertical-align:middle;}

The Goods

The Bads

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!

<div id="content">
	Content Here		
</div>
#content {position:absolute; top:50%; height:240px; margin-top:-120px; /* negative half of the height */}

The Goods

The Bads

Method 3

In this method, we will insert a div above the content element. This will be set to height:50%; and margin-bottom:-contentheight;. The content will then clear the float and end up in the middle.

<div id="floater"></div>
<div id="content">
	Content Here		
</div>
#floater	{float:left; height:50%; margin-bottom:-120px;}
#content	{clear:both; height:240px; position:relative;}

The Goods

The Bads

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.

<div id="content">
	Content Here		
</div>
#content {position:absolute; top:0; bottom:0; left:0; right:0;
			margin:auto; height:240px; width:70%;}

The Goods

The Bads

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

The Bads

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.

<div id="top">
	<h1>Title</h1>
</div>
<div id="floater"></div>
<div id="content">
	Content Here		
</div>
#floater	{float:left; height:50%; margin-bottom:-120px;}
#top		{float:right; width:100%; text-align:center;}
#content	{clear:both; height:240px; position:relative;}

Now you know how it works, let’s start creating a simple but interesting website! The final product will look something like this:

Step 1

It's always good to start with semantic markup. This is how our page will be structured:

Here is the xhtml code I will be using:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<title>A Centred Company</title>
	<link rel="stylesheet" href="styles.css" type="text/css" media="all" />
</head>

<body>
	<div id="floater"></div>
	<div id="centered">
		
		<div id="side">
			<div id="logo"><strong><span>A</span> Company</strong></div>
			<ul id="nav">
				<li><a href="#">Home</a></li>
				<li><a href="#">Products</a></li>
				<li><a href="#">Blog</a></li>
				<li><a href="#">Contact</a></li>
				<li><a href="#">About</a></li>
			</ul>
		</div>
		
		<div id="content">
		
			<h1>Page Title</h1>
			
			<p>
			Holisticly re-engineer value-added outsourcing after process-centric collaboration and idea-sharing. 
			Energistically simplify impactful niche markets via enabled imperatives. 
			Holisticly predominate premium innovation after compelling scenarios. 
			Seamlessly recaptiualize high standards in human capital with leading-edge manufactured products. 
			Distinctively syndicate standards compliant schemas before robust vortals. 
			Uniquely recaptiualize leveraged web-readiness vis-a-vis out-of-the-box information. 
			</p>
			
			<h2>Heading 2</h2>
			
			<p>
			Efficiently embrace customized web-readiness rather than customer directed processes. 
			Assertively grow cross-platform imperatives vis-a-vis proactive technologies. 
			Conveniently empower multidisciplinary meta-services without enterprise-wide interfaces. 
			Conveniently streamline competitive strategic theme areas with focused e-markets. 
			Phosfluorescently syndicate world-class communities vis-a-vis value-added markets. 
			Appropriately reinvent holistic services before robust e-services. 
			</p>
		
		</div>
		
	</div>
	
	<div id="bottom">
		<p>
			Copyright notice goes here
		</p>
	</div>
</body>
</html>

Step 2

Now we will start with some basic CSS to lay out the page. You should put this in styles.css, which is linked to at the top of our html.

html, body {
	margin:0; padding:0;
	height:100%;
}

body {
	background:url('page_bg.jpg') 50% 50% no-repeat #FC3;
	font-family:Georgia, Times, serifs;
}

#floater {
	position:relative; float:left;
	height:50%;	margin-bottom:-200px;
	width:1px;
}

#centered {
	position:relative; clear:left;
	height:400px; width:80%; max-width:800px; min-width:400px;
	margin:0 auto;
	background:#fff;
	border:4px solid #666;
}

#bottom {
	position:absolute;
	bottom:0; right:0;
}

#nav {
	position:absolute; left:0; top:0; bottom:0; right:70%;
	padding:20px; margin:10px;
}

#content {
	position:absolute; left:30%; right:0; top:0; bottom:0;
	overflow:auto; height:340px;
	padding:20px; margin:10px;
}

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.

#nav ul {
	list-style:none;
	padding:0; margin:20px 0 0 0; text-indent:0;
}

#nav li {
	padding:0; margin:3px;
}

#nav li a {
	display:block; background-color:#e8e8e8;
	padding:7px; margin:0;
	text-decoration:none; color:#000;
	border-bottom:1px solid #bbb;
	text-align:right;
}

#nav li a::after {
	content:'\00BB'; color:#aaa; font-weight:bold;
	display:inline; float:right;
	margin:0 2px 0 5px;
}

#nav li a:hover, #nav li a:focus {
	background:#f8f8f8;
	border-bottom-color:#777;
}

#nav li a:hover::after {
	margin:0 0 0 7px; color:#f93;
}

#nav li a:active {
	padding:8px 7px 6px 7px;
}


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.

#centered {
	-webkit-border-radius:8px; -moz-border-radius:8px; border-radius:8px;
}

h1, h2, h3, h4, h5, h6 {
	font-family:Helvetica, Arial, sans-serif;
	font-weight:normal; color:#666;
}

h1 {
	color:#f93; border-bottom:1px solid #ddd;
	letter-spacing:-0.05em; font-weight:bold;
	margin-top:0; padding-top:0;
}


#bottom {
	padding:10px;
	font-size:0.7em;
	color:#f03;
}


#logo {
	font-size:2em; text-align:center;
	color:#999;
}

#logo strong {
	font-weight:normal;
}

#logo span {
	display:block;
	font-size:4em; line-height:0.7em;
	color:#666;
}

p, h2, h3 {
	line-height:1.6em;
}

a {
	color:#f03;
}

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:

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.

<!--[if lte IE 6 ]>
	<link rel="stylesheet" href="styles_ie6.css" type="text/css" media="all" />
<![endif]-->

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:

<!--[if IE 5 ]>
	<link rel="stylesheet" href="styles_ie5.css" type="text/css" media="all" />
<![endif]-->

<!--[if lte IE 6 ]>
	<link rel="stylesheet" href="styles_ie6.css" type="text/css" media="all" />
<![endif]-->

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.

/* styles_ie5.css */

body		{text-align:center;}
#centered	{text-align:left;}

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.

/* styles_ie6.css */

#nav	{ zoom:1; }
#nav li	{ zoom:1; }
#nav a	{ zoom:1; }

#centered {width:expression( (document.body.clientWidth > 1000) ? "800px" : "80%");}

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%”

Our statement uses a ternary operator (like an “if” statement). They are structured like: condition ? value if true : value if false

This is the equivalent of:

if (document.body.clientWidth > 1000) {
	"800px";
} else {
	"80%";
}

More Ideas

There are many interesting things you can do with centered websites. I used this idea in my redesign of the SWFObject Generator 2.0 (to generate code to use SWFObject 2.0). Here is also another idea.

Summary

So, in the end we have learnt:

Sources

I did not discover all this myself. Here are some of the articles I have read which describe some of these techniques: (If you are interested, I recommend you read them)