Centering in the Unknown

Avatar of Chris Coyier
Chris Coyier on (Updated on )

📣 Freelancers, Developers, and Part-Time Agency Owners: Kickstart Your Own Digital Agency with UACADEMY Launch by UGURUS 📣

When it comes to centering things in web design, the more information you have about the element being centered and its parent element, the easier it is. So what if you don’t know anything? It’s still kinda doable.

Not too hard: Known Child

If you know the height and width of both the element to be centered and its parent element (and those measurements won’t change, i.e. not fluid width environment) one foolproof way to center the element is just to absolute position it with pixel values so it looks perfectly centered.

Let’s say you know the exact width and height of the element you are centering, but the parent element can change in height and width.

You absolutely position the element to be centered and set the top and left values to 50% and the margin top and left values to negative half of the elements height and width. That was a tounge twister, so check this out.

Harder: Unknown Child

The hard comes in when you don’t know the dimensions of the element to be centered.

What do we know? Nothing! When do we know it? Now!

The grossest way to handle it is literally tables:

<table style="width: 100%;">
  <tr>
     <td style="text-align: center; vertical-align: middle;">
          Unknown stuff to be centered.
     </td>
  </tr>
</table>

If you are worried about the semantics of that, you could attempt to match it to your content.

<div class="something-semantic">
   <div class="something-else-semantic">
       Unknown stuff to be centered.
   </div>
</div>

And get the same result as the tables like:

.something-semantic {
  display: table;
  width: 100%;
}
.something-else-semantic {
  display: table-cell;
  text-align: center;
  vertical-align: middle;
}

CSS tables might be fine for you. Or it might not. Tables do render a bit differently than just a regular block-level div does. For instance the 100% width thing. A table will only stretch to be as wide as it needs to for the content inside it whereas by default a block level element will expand to the width of its parent automatically. Also, god help you if you need other content inside that div that you want to position or otherwise not act as a table-cell.

Michał Czernow wrote in to me with an alternate technique that is extremely clever and accomplishes the same thing. If we set up a “ghost” element inside the parent that is 100% height, then we vertical-align: middle both that and the element to be centered, we get the same effect.

See what we did there?

So does that ghost element need to be an un-semantic element? Nope, it can be a pseudo element.

/* This parent can be any width and height */
.block {
  text-align: center;

  /* May want to do this if there is risk the container may be narrower than the element inside */
  white-space: nowrap;
}
 
/* The ghost, nudged to maintain perfect centering */
.block:before {
  content: '';
  display: inline-block;
  height: 100%;
  vertical-align: middle;
  margin-right: -0.25em; /* Adjusts for spacing */
}

/* The element to be centered, can also be of any width and height */ 
.centered {
  display: inline-block;
  vertical-align: middle;
  width: 300px;
}
View Demo

I’d like to tell you the ghost element technique is way better and should be the go-to centering technique for the ages. But in reality, it’s just about the same as the table trick. The browser support for this is essentially everything and IE 8+. IE 7 doesn’t support pseudo elements. But it doesn’t support CSS tables either, so it’s a horse apiece. If IE <= 7 support is needed, it's <table> time (or use an equally un-semantic <span> or something for the ghost element).

This stuff isn’t brand new territory. Gary Turner wrote about it like 5 years ago. But I credit Michał for doing it with a pseudo element and making it the most semantic approach yet.

Note: The 0.25em nudge-back is a little janky. To do it perfectly, you could set font-size: 0; on the parent and then notch the font size back up inside the content container.