Vertical alignment in css

2014-02-13

And while HTML will celebrate its 23rd anniversity this year, it's still a big pain to center something vertically. Oh yes, I went there. I had to. I find it hard to believe that over all this time nobody that could figure "hey, people need this, let's make it happen". No instead they went on and came up with super complex ideas, implemented that, but left vertical alignment to the hacks. Ugh.

So, I wanted to center the block on http://c80.nl so I needed to dive into this clusterfuck again. It had been a while so I was hoping for progress. Much to my surprise, there was! It's called flexbox. It's still quite verbose, and requires styles on two elements. That makes it only slightly better than the 'ol display:table hack, only because flexbox was actually meant this kind of thing. Although, still not really, it's just one of the gazillion things it can do. In fact, when I think about it, flexbox is just the new table and using it to argue vertically centering is solved is just as bad as saying that tables are fine for it. So there.

Okay so I've got a few ways of fluidly centering content for ya. And also a couple of ways for fixed content. There's probably many more hacks out there, so don't worry, this is not a complete list. And since we're talking hacks in one way or the other, I'll skip the whole semantic argument from here on out.

display:flex


So basically what you do is make the parent element display:flex, combined with align-items: center; and justify-content: center;. That's it for the parent. The child ... just needs to be there in one container. Maybe in a few months, when Firefox supports flex-wrap: wrap, you don't actually need that either. But then you'll have to add more css to make sure it all works. Not sure if that's worth the effort. But that's up to you.

Using this flexbox approach seems to actually work on all current browsers, IE10+ included. My mobile and iPad (on ios 5) both seem to support it properly too. You can see c80.nl for an example. The box stays centered and if the viewport shrinks, it'll nicely stick to the top-left. This is much better than the old 50% fixed width margin hack. Mainly because it's fluid :)

Here's the entire boilerplate:

Code:
.parent {
/* this is just:
display: flex;
align-items: center;
justify-content: center;
*/
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
-moz-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-moz-box-pack: center;
-ms-flex-pack: center;
justify-content: center;

Oh, the boilerplate. But it works fine on body. There's a bit more to flexbox if you're interested, you can read it here.

display:table


The table hack is a bit older, and thus browser support is a bit better. Obviously it's a super hack, but it seems to work across the board. Basically you set the parent to display:table and the child to display:table-cell; vertical-alignment: middle; ("So _that's_ where you can use vertical-align!" Yes.)

The downside to this is that you'll _need_ this container div to center the content. And of course you're basically using tables anyways, minus the verbose tags.

Code:
.parent { display:table; width: 100%; height: 100%; }
.container { display: table-cell; vertical-align: middle; }

I needed the 100%, might not apply to you.

line-height


The last one is just for centering text. I know it's kind of a disappointment, and yeah it's an old hack. But many people don't know this yet. If you have some text you want vertically centered and you have a fixed height, you can set line-height: <height>; on it, to whatever the height is.

Unfortunately this won't work for glyphs that aren't centered like * or _, and it won't work for inputs neither. Notorious are checkbox and radio inputs, which for some reason refuse to properly align themselves at the font base or center.

Code:
.div { height: 30px; line-height: 30px; }

Yeah, I'm still in the px era.

50% fixed


Okay okay, the old hack which you can only use for fixed heights is to set position: relative; margin-top: -50%; top: <height/2>;. This will properly center your content but there are two downsides. One is minor; you might need to set position relative to one of the ancestors. The other one is bigger; if you resize the viewport to smaller than the size of whatever you were centering, you'll see that it won't stick to the left, but rather it will hide in the left. Up to 50%, in fact. Because that's what the negative margin does. You could of course use @media checks to prevent this though.

This is how I centered in the very old days.

Code:
.centered {
position: relative;
height: 100px;
width: 200px;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -100px;
}

You can also set position:fixed for the same effect:

Code:
.centered {
position: fixed;
height: 100px;
width: 200px;
top: 50%;
left: 50%;
margin-top: -50px;
margin-left: -100px;
}

(Details)

absolute center


This is something I just stumbled upon, you're on your own here.

Code:
.Absolute-Center {
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}

source, from source.

margin:auto


Just in case you didn't know; fluidly centering stuff these days is done through margin: 0 auto;. This only works for horizontal elements, and only those in the flow. So no floats or weird positioning. Doesn't work for vertical stuff because their margins would be 0 anyways.

Code:
.block { margin: 0 auto; }

Easy centering.

vertical-align: middle;


<rant>

Let's circle back to the real problem here: vertical-align. It doesn't work as advertised. No, actually that's not right. It's working as advertised but it should not have been limited to table-cell elements. I mean, really, when we center something do we want to apply any of the above hacks? Or do we just want to say vertical-align: middle;? I know what I'd prefer.

So come on, let's start implementing this trivial thing. I'm sure it's easier than other proposals being worked on. Free us of the cruft, it proves you can do it already anyways. Why not make it easier for everybody.

(And maybe allow vertical-align: center; for the sake of all things consistent? But that's just a feature request ;))

</rant>
</post>