Horse farm on Chapman Rd, Delaware County. Photo: Will Koehler
By
— 7 min read

Vertically Align Content With CSS Using This One Weird Trick

Front-end devs will hate me for revealing the secret.

Phil Karlton famously said “there are only two hard things in computer science: cache invalidation and naming things”. I would add a third: vertically aligning content with CSS.

The scenario

We want to vertically align variable height content within a wrapper div.

.wrapper {
  height: 150px;
  border: 1px solid #CCC;
}

.content {
  display: inline-block;
  border: 1px solid #493ACC;
}
<div class='wrapper'>
  <div class='content'>
    This should be<br>
    vertically centered
  </div>
  <div class='content'>
    Also this
  </div>
</div>
This should be
vertically centered
Also this

Just use vertical-align:middle?

Most people (myself included) expect that adding vertical-align:middle to the wrapper will do this trick. But vertical-align doesn’t set the alignment of content within a wrapper, it sets the alignment of inline elements relative to each other.

Let’s try some variations of vertical-align and see how it works.

<span class='text big'>BIG text</span>
<span class='text small'>SMALL text</span>
.text.big { font-size: 40px; }
.text.small { font-size: 18px; }

.text { vertical-align: baseline; }   /* this is the default */
BIG text SMALL text
.text { vertical-align: top; }
BIG text SMALL text
.text { vertical-align: bottom; }
BIG text SMALL text
.text { vertical-align: middle; }
BIG text SMALL text

Making vertical-align:middle work for you

Going back to the original scenario, instead of applying vertical-align:middle to the wrapper, let’s apply vertical-align:middle to the content. Seems counter-intuitive, but bear with me.

.content {
  display: inline-block;
  border: 1px solid #493ACC;
  vertical-align: middle;
}
This should be
vertically centered
Also this

We’re getting closer. Let’s add a third element with 100% height

<div class='wrapper'>
  <div class='content' style='height:100%'>&nbsp;</div>
  <div class='content'>
    I would like this<br>
    to be vertically centered
  </div>
  <div class='content'>
    Also this
  </div>
</div>
 
This should be
vertically centered
Also this

This is starting to look promising!

Make CSS do the work for us

Manually adding a third element to vertically align things is a burden. So let’s have css do that for us with a :before pseudo element.

.wrapper:before {
  content: '';
  display: inline-block;
  height: 100%;
  vertical-align: middle;
  border: 1px solid red; /* so we can see what's going on */
}
<div class='wrapper'>
  <div class='content'>
    I would like this<br>
    to be vertically centered
  </div>
  <div class='content'>
    Also this
  </div>
</div>
This should be
vertically centered
Also this

It’s working! Now remove the red border from the pseudo element and… Voila

This should be
vertically centered
Also this

Edge cases

Well this is awesome. But you knew there would be edge cases right? Consider this scenario:

<div class='wrapper'>
  <div class='content'>
    This text should wrap as needed
  </div>
</div>
This text should wrap as needed

Your customer views the page on a small device, or resizes their browser and…

This text should wrap as needed

What happened?? The text wraped, but the content dropped below the wrapper. Let’s make our before pseudo element visible again.

This text should wrap as needed

Now we can see what happened. The :before pseudo element is an inline-block element. So it’s getting wrapped as well. We can fix that with another trick. Add a negative right margin to the :before pseudo element so the pseudo element doesn’t take up any horizontal space.

.wrapper:before {
  content: '';
  display: inline-block;
  height: 100%;
  vertical-align: middle;
  margin-right: -.35em;
}

Now the text is wrapping like it should.

This text should wrap as needed

All together now

.wrapper {
  height: 150px;
  border: 1px solid #CCC;
}

.wrapper:before {
  content: '';
  display: inline-block;
  height: 100%;
  vertical-align: middle;
  margin-right: -.35em;
}            

.content {
  display: inline-block;
  border: 1px solid #493ACC;
}
<div class='wrapper'>
  <div class='content'>
    This should be<br>
    vertically centered
  </div>
  <div class='content'>
    Also this
  </div>
</div>

Conclusion

I’ve used many tricks over the years to vertically align content. They all have tradeoffs. Some add a lot of noise to the CSS. Others (like using display:table-cell) have side effects. I’ve just recently discovered this trick, but I think it’s going to be my new go-to solution. In terms of compatibility, it works on all modern browsers + IE8.

Thanks to Chris Coyler for the idea and Gary Turner for the original work.