Centring elements horizontally or vertically often works in mysterious ways and one of the seemingly simple layout problems that can make CSS so frustrating to use. There are several techniques to cope with different needs but combining absolute positioning with unknown dimensions usually conspires to create absolute developer misery. This is a battle-tested dropdown menu able to cope with an unknown number of option columns and always centred relative to its toggle using only IE8-compatible CSS.
The markup
There’s nothing remarkable about the markup, it’s a standard nested navigation structure, only with the addition of an extra wrapping element. I’ve used the BEM naming convention to avoid nesting CSS selectors and to make the code as self-documenting as possible. For extra design embellishments such as the outlined arrow it’s sensible to add an extra wrapper.
The CSS
Firstly, to use multiple columns without specifying the widths of them or any parent elements requires the use of table layout which I’ve written about in detail before. Table columns cannot be collapsed onto multiple rows and so any parent elements without a defined width will be sized to contain its children.
Aligning the dropdown as centred relative to the toggle is achieved in three steps:
- The dropdown ‘root’ element creates a new containing block by declaring
position: relative
and shrink wraps the dropdown toggle by being displayed as an inline block. - The dropdown ‘panel’ is positioned absolutely and shifted 50% from the left edge of its containing block (the root element).
- A negative margin of 50% is applied to the dropdown panel ‘wrapper’ and because the percentage value is calculated relative to the width of its parent (the panel) it is shifted perfectly back into the centre.
The complete CSS skeleton for the menu is below. I haven’t included any visual embellishments such as the shadows or arrows, just the basic layout.
And that’s it, no need to hard code any widths and certainly no need to bring Javascript into layout concerns. Check out the full example source for some fun with CSS arrows ;-).