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:
.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.
.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
input
s neither. Notorious are checkbox and radio inputs, which for some reason refuse to properly align themselves at the font base or center.
.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.
.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:
.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.
.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.
.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>