A Professional Guide: How to Vertically Align Elements Using CSS
Anybody who is a web designer will have come across the age old problem that is vertical alignment. I have been working in the world of web for nearly a year now and have been asked by clients to “centre this in the middle of the window” or “make that sit in the middle of that” numerous times already. Horizontally, it’s easy using CSS:
display: block; margin: 0 auto; /* Bob: is your uncle; */ display: inline-block; text-align: center; /* Boom! */
Why can’t it be this easy? The problem is websites aren’t traditionally meant to be aligned vertically. Web pages scale widthways, and the height alters to accommodate the content. Fortunately for us there are various methods using CSS to work around this problem. Some work better than others, some are plain awful and should not be used. Remember it’s all relative (sometimes absolute), what works in one situation might not work in another.
Firstly, the vertical-align property
All designers have had that moment when they first discover the vertical-align property, then minutes later are left wondering what the point of having it is, especially when the bloody thing doesn’t work!!! Sure enough we have all tried to use it incorrectly at one time or another. But no more!
vertical-align dictates how siblings lined up in a row are aligned in relation to each other. It can be applied to inline and table cell elements.
Values for inline elements:
baseline: aligns the baseline of the text of the children with the baseline of their parent. Imagine the baseline as the lines on lined paper. This is selected by default.
bottom: aligns the bottom of the children to the bottom of the entire line.
central: aligns the central baseline of the children with the central baseline of their parent.
middle: aligns the middle baseline of the children with the middle baseline of their parent.
sub: aligns the baseline of the children with the subscript-baseline of its parent.
super: aligns the baseline of the children with the superscript-baseline of its parent.
text-bottom: aligns the bottom of the children with the absolute bottom of the parent elements font.
text-top: aligns the top of the children with the absolute top of the parent element’s font.
top: aligns the top of the children with the top of the entire line.
length: aligns the baseline of the children at the length provided above the baseline of their parent.
vertical-align: middle is usually the culprit when it’s being misused, the most common reason being its use on a block element. Remember, it will only work on inline or inline-block elements. If you use it on a block element all its children will inherit the value.
Vertical-align can also be used with HTML table cells. Values for table cells:
- top: aligns the cells contents with the top of the cell.
- middle: aligns the cells contents with the middle of the cell.
- bottom: aligns the cells contents with the bottom of the cell.
Now that’s been cleared up, let’s take a look at how we can fix our block elements. Line height This is a simple technique that works well when you want to centre one line of text. It also works with images, although it’s a hack and is probably best avoided.
The example consists of two elements, a parent and a child container. The line height is set on the child container which in turn forces its parent to resize to the same height. Be aware that for this to work the line height has to be greater than the font size or the image height. In order to use it on images, you need to add vertical-align: middle to your image. Like I said this is a hack, there are methods I cover later on that produce the same result.
Easy to implement.
Only works with one line of text.
It's a bit of a dirty hack when it's used for anything other than text.
CSS table properties
CSS table properties are not HTML tables. We can use display: table to make an element act like a table. These properties are based on the original HTML table elements and behave in the same way.
In the demo the parent container is acting as a table, and the child container as a table cell. As the cell inherits the height from its parent, when vertical-align: middle is applied it centres the cell perfectly.
Works exactly like HTML tables and is semantically correct.
Doesn’t need defined heights and widths.
Table stretches to the width of the content inside it unless a width is defined.
It gets messy if you need another block element inside the parent container and you don’t want it to display as a table cell.
It is possible to do this using HTML tables, however it should be avoided as using HTML elements to style a web page is a big no no, that’s what CSS is for. It is also semantically incorrect, unless your content belongs in a table, e.g. a train timetable.
In order to use position: absolute to vertically align your chosen element a height needs to be defined, either in pixels or percentages.
There are two ways to centre you child container. The first option, illustrated above, involves moving the child down using top: 50% and moving it up half its height with transform: translateY(-50%) so that the centre of the child container is lined up with that of its parent. This can also be done using negative margins. The second option is to give the child container a top, bottom, left and right value of 0 and set the margins to auto. However I have had mixed results with this method and prefer using the first option.
Dimensions can be defined in percentages which makes it responsive.
Need to define a height, sometimes it’s not possible.
If there is more than one child there will be overlapping issues unless they too are absolutely positioned.
You must remember to position your parent container relatively or absolutely otherwise your child container won’t centre.
The 'Ghost Element'
This technique was originally mentioned in the CSS Tricks blog article ‘Centering in the Unknown'. A ghost element is used to centre the child container within it’s parent. As it is given a height of 100%, there is no need to define heights for parent or child. It is an ideal method to use when the dimensions are unknown.
As Chris Coyier mentions in his blog post, Michal Czernow suggests using a pseudo element as the ghost, eliminating the need for another div and making it semantically correct.
Works exactly like CSS tables. Neither technique is better than the other.
Flexbox is a relatively new addition to the CSS family. I don’t want to go into too much detail, and there are a lot of details, so here is a quick description of what it does. According to www.w3.org:
“In the flex layout model, the children of a flex container can be laid out in any direction, and can "flex" their sizes, either growing to fill unused space or shrinking to avoid overflowing the parent. Both horizontal and vertical alignment of the children can be easily manipulated. Nesting of these boxes (horizontal inside vertical, or vertical inside horizontal) can be used to build layouts in two dimensions.”
To summarise, the flexbox property is exclusively for small scale layouts. It is easy to divide the child elements equally (and unequally) into rows or columns, change the direction of the flow and much more. Vertically aligning a single element in a container is extremely simple.
When the parent container is turned from a block to a flex element, its children are automatically turned into flex items. Setting the margin to auto on the child pushes it to the centre of its parent. Adding more children will not affect its vertical alignment although it will arrange itself in relation to its siblings. For example, if there are too many children to fit on one row and the parent container has the property flex-wrap: wrap the children that can’t fit will drop to the next row and all children will be centred within their parent as one unit.
Flexbox has been made to tackle layout issues and is therefore perfect for vertical alignment.
Although it is compatible with all the latest browsers using prefixes, older versions are not supported, e.g. IE9.
If you want to learn more about flexboxes, take a look at CSS Tricks’ 'A Complete Guide to Flexbox'.
With so many options it’s a wonder why we find it so difficult to vertically align anything! Unfortunately there is no one solid solution. Flexbox looks the most promising, the only drawback is it’s compatibility with older browsers. However I think it will be our goto method once the specification has been nailed.