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>
vertically centered
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 */
.text { vertical-align: top; }
.text { vertical-align: bottom; }
.text { vertical-align: middle; }
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;
}
vertically centered
We’re getting closer. Let’s add a third element with 100% height
<div class='wrapper'>
<div class='content' style='height:100%'> </div>
<div class='content'>
I would like this<br>
to be vertically centered
</div>
<div class='content'>
Also this
</div>
</div>
vertically centered
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>
vertically centered
It’s working! Now remove the red border from the pseudo element and… Voila
vertically centered
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>
Your customer views the page on a small device, or resizes their browser and…
What happened?? The text wraped, but the content dropped below the wrapper. Let’s make our before pseudo element visible again.
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.
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.