Why is it so hard to vertically align things with HTML and CSS?
If I had to pick one task in web development that most consistently trips me up, it would have to be vertically aligning different forms of content (e.g. an image and text). The thing that I find most frustrating is how wrong the outcome can be if you don’t have the CSS incantations exactly correct, while simultaneously, from the code perspective, looking like everything should Just Work.
My goal with this blog post is to dip a toe into the world of pain that is commonly referred to as CSS and why vertically aligning things doesn’t work the way you might expect…
Here’s our setup. A div with two elements in it. An image and a span.
<div class=”widget”> <img src=”your_sweet_image.png” /> <span>Check out this picture!</span> </div>
Simple enough, right?
But now we have a problem, the text sits at the bottom of the image which doesn’t look very good. We want the text to be visually centered with the picture. The vertical-align css property value “middle” seems like it should do the trick. We can just apply that to the text to vertically align it to the image, right? Let’s give that a shot.
That’s clearly not what we wanted. If we look closely we can see that it’s actually worse than it was before!
Well, vertically-aligning the text actually moved it down so that doesn’t seem right. Let’s try just vertically aligning the image.
That looks better, but changing the property on the image actually just moved the text!? Also the text still isn’t quite in the middle. It’s moved too far. Vertically aligning the text before seemed to lower the text so let’s try vertically aligning both the image and the text.
Ah, nice and vertically aligned.
So what exactly is going on here? Why does vertically aligning the text move it down? Why did applying a style to the image affect the text next to it??
There’s a couple different things going on here which bring about this seemingly nonsensical behavior. Once you understand the different parts, though, it makes a bit more sense.
First, we need to get back to basics. HTML, at its inception, was designed to lay out paragraphs of text. The first version didn’t even support images!
Unsurprisingly then, there’s a strong concept of a line for all elements that are considered to be “inline” which <span> and <img> both are. The vertical-align CSS attribute defines the vertical position of the element with respect to the line not with respect to the other elements in the line.
Another important fact is that the browser fills up space eagerly unless you tell it do otherwise. It will render a line the first place vertically it will fit.
These two facts combine to produce the behavior we see.
Let’s go back to the earlier example now with the aid of the line drawn for you. The bottom of the image and the baseline of the text are aligned with the line. When we set vertical-align: middle on the text it is vertically centered around the line, not with respect to the sibling <img> element.
We had another mystery which was that a setting on the image seemed to only affect the text. This relates to the second point above. When the image aligns its center around the line, the browser says “hmmm it looks like I can draw this whole line higher”, the line moves up until the image is touching the top of the document again which is why it doesn’t appear to move. The text comes along with it, its baseline still aligned with the line.
Here is an exaggerated animation of the image’s transition from “vertical-align: baseline” to “vertical-align: middle”:
Finally, when we vertically align the text we have visual alignment for the image and the span since they’re both splitting the difference around the line.
So it turns out there is a method to the madness after all. Hopefully this insight will save you some frustration the next time you’re fighting with your CSS :)