Justified and Vertically Centered Header Elements

A little journey into positioning header elements to be centered vertically and justified with the help of pseudo-elements.
Justified and Vertically Centered Header with Pseudo-Elements

From our monthly sponsor: Automate manual QA and catch visual bugs with Percyโ€™s all-in-one visual testing and review platform.

Sometimes the most simple looking little layouts require complicated structures and various styling tricks. Centering elements, justifying content or aligning them the way we want can get really funky and frustrating. One of those little simple things that comes with many pitfall when laying it out is the justified header: title on the left and navigation on the right side. You could use floats, position one of the elements absolutely and more, no big deal. But now you’d like to center both things vertically. Easy, just add paddings, margins or set the line-height to the height of the parent, right? There are surely many solutions but when you start to think “responsively”, not all of these solution are so easy after all and they might bring some undesired effects. Especially the combination of both, vertical centering and justification, can get quite messy.

Today I don’t want to go through all of the different possibilities of centering header (or any other) elements vertically and justifying them. I just want to show you some simple CSS tricks that will do it right, in my opinion. Not much markup, no absolute elements and no floats. Just one magic word: pseudo-elements.

So, we want to create a header with the following look:

Header01

The markup is simply

<header>
	<h1>Super Bad</h1>
	<nav><a>First Link</a><a>Second Link</a><a>Third Link</a></nav>
</header>

We want the header to have a fixed height and some padding. It also needs a text-align: justify because we want to spread the children:

header {
    text-align: justify;
    letter-spacing: 1px;
    height: 8em;
    padding: 2em 10%;
    background: #2c3e50;
    color: #fff;
}

In order to spread the heading and the nav, we will first set them both to inline-block. Like that we can put them next to each other without using floats or absolute positioning.

header h1,
header nav {
    display: inline-block;
}

But for the text-align: justify to work as we wish (setting the heading on the left and the nav on the right), we need to use a little pseudo-element trick that I’ve found in this great article by Jelmer de Maat. Justifying inline or inline-block elements only works if their length is actually bigger than the one of their container, i.e. once we have an overflow or line-break. But since this is not the case here, the content is not justified. Here we will leverage the power of pseudo-elements: we can give the pseudo-class ::after a width of 100% and set it to inline-block. This will cause a line-break and our content will get justified like we wanted:

header::after {
    content: '';
    display: inline-block;
    width: 100%;
}

So this it what we have right now:

JS Bin

Nice, we’ve positioned both elements horizontally as we wanted without using floats or absolute positioning. But now we’d also like to center them vertically. I can hear your alarm bells going off, you’ve surely fought with this many times: vertical centering. There are many attempts and solutions but they all come with some downsides or just seem too complicated. At this point, you might think about margins, line-height and of course absolute positioning.

Let’s try to find a better way.

If you play around with vertical-align on the nav, you will notice that it will move vertically relative to the height of the heading. For example, let’s try vertical-align: top:

JS Bin

As you can see, the nav moves up.

If we set the height of the heading to 100%, we can center the nav relative to the header height by setting vertical-align: middle:

JS Bin

Now, how can we make both elements be centered inside of the header container? We would need another element that would make the vertical-align work as we want. I can hear you thinking now… Maybe a pseudo-element? Yes, of course! Again, we will use a pseudo-element to create the desired environment for our properties to work, a technique by Michał Czernow mentioned in the article Centering the Unknown. So, we will add a pseudo element that has a height of 100% and a vertical-align of middle:

header h1 {
    height: 100%;
}

header h1::before {
    content: '';
    display: inline-block;
    vertical-align: middle;  
    height: 100%;
}

And as a result we get what we want:

JS Bin

Almost! It does look right, but we need to make sure of two more things:

  1. line-breaks for longer text
  2. responsiveness

So, let’s start with number one. If our title would be much longer, our pseudo-element would cause an undesired effect which is to push the heading from the second line on down. That’s of course logical since we have a pseudo-element with a height of 100%:

JS Bin

(Note that we’ve, of course, restricted the width of the h1 to 50% because we don’t want it to just fill all the header.)

Let’s fix that by wrapping the heading into another division…

<header>
	<div><h1>Super Bad</h1></div>
	<nav><a>First Link</a><a>Second Link</a><a>Third Link</a></nav>
</header>

…and using the pseudo-element trick on that element:

header {
    text-align: justify;
    height: 15em;
    padding: 2em 5%;
    background: #2c3e50;
    color: #fff;
}

header::after {
    content: '';
    display: inline-block;
    width: 100%;
}

header > div,
header nav,
header div h1 {
    display: inline-block;
    vertical-align: middle;
}

header > div {
    width: 50%;
    height: 100%;
    text-align: left;
}

header > div::before {
    content: '';
    display: inline-block;  
    vertical-align: middle;
    height: 100%;
}

And we get this:

JS Bin

Number one is off the list, so let’s take care of the responsiveness. There are of course, several possibilites when it comes to flexible layouts. You could for example, not define a height for the header at all and let the inner elements and the padding be responsible for the height. In that case we also won’t need the second pseudo-element trick (and the wrapper for the h1) and we would simply have:

header {
    text-align: justify;
    padding: 2em 5%;
    background: #2c3e50;
    color: #fff;
}

header::after {
    content: '';
    display: inline-block;
    width: 100%;
}

header h1,
header nav {
    display: inline-block;
    vertical-align: middle;
}

header h1 {
    width: 50%;
    text-align: left;
    padding-top: 0.5em;
}

header nav {
    padding-top: 1em;
}

Which looks like this:

JS Bin

This will adjust just fine on smaller screens. But if you need a defined height (for example, just think about a full window height header) then we need our second pseudo-element trick, the wrapper and a media query that will simply take care of the smaller screens and maybe center the elements:

@media screen and (max-width: 820px){
    
    header {
        height: auto;
    }
    
    header > div,
    header > div h1,
    header nav {
        height: auto;
        width: auto;
        display: block;
        text-align: center;
    }
    
}

With the following result:

JS Bin

Note that I’m using 820px here because I want to show you what happens beyond that breakpoint. You should of course use an appropriate value for your layout.

If you’d like to support IE8, you should use “:” instead of “::” for the pseudo-elements.

Let’s give this baby some love and finalize the style:

@import url(http://fonts.googleapis.com/css?family=Lato:400,700italic);
* { padding: 0; margin: 0; }
body { background: #1abc9c; font-family: 'Lato', sans-serif; text-transform: uppercase; letter-spacing: 1px;}

header {
    text-align: justify;
    height: 8em;
    padding: 2em 5%;
    background: #2c3e50;
    color: #fff;
}

header::after {
    content: '';
    display: inline-block;
    width: 100%;
}

header > div,
header > div::before,
header nav,
header > div h1 {
    display: inline-block;
    vertical-align: middle;
    text-align: left;
}

header > div {
    height: 100%;
}

header > div::before {
    content: '';
    height: 100%;
}

header > div h1 {
    font-size: 3em;
    font-style: italic;
}

header nav a {
    padding: 0 0.6em;
    white-space: nowrap;
}

header nav a:last-child {
    padding-right: 0;
}

@media screen and (max-width: 720px){
    
    header {
        height: auto;
    }
    
    header > div,
    header > div h1,
    header nav {
        height: auto;
        width: auto;
        display: block;
        text-align: center;
    }
    
}

And here we go:

JS Bin

And that’s it! We’ve centered the elements vertically and aligned them as we wanted.

This is of course just one way to do it and there are surely tons of possibilities. I just found this one to be really neat.

Hope this was somehow useful to you!

Tagged with:

Mary Lou

ML is a freelance web designer and developer with a passion for interaction design. She studied Cognitive Science and Computational Logic and has a weakness for the smell of freshly ground peppercorns.

http://www.codrops.com

Receive our bi-weekly Collective or official newsletter right in your inbox.

CSS Reference

Learn about all important CSS properties from the basics with our extensive and easy-to-read CSS Reference.

It doesn't matter if you are a beginner or intermediate, start learning CSS now.

Feedback 75

Comments are closed.
  1. Beautifully elegant solution, Mary Lou. Too often I see solutions stop mid-way and not take into account responsiveness or multi-line text. Once again, you have offered a way that is flexible and future-thinking.

  2. Why not simply set line-height: 8em to the header instead of pseudo-element tricks for vertical centering (as in this bin)?

    • Setting the line-height to the height of the header is not a solution in my opinion (think about multi-line text). In this case it looks alright because the h1 is a short text. But if you increase it, you’ll get into this problem. I guess it all comes down to what the context is and how you want your text to behave. Thanks for your feedback, cheers, ML

    • Mary Lou, thank you for your reply! I’m still not convinced that introducing extra boxes into the layout is the more effective way to deal with multi-line heading than just resetting the line-height for it… but I agree that it depends much on the context.

    • Think about in a a theme/CMS situation where you don’t know how many lines it’s breaking to or when it’s breaking – Mary’s solution works in that case.

    • Jack, both solutions work as long as we know the height of the header. Mary’s solution has the advantage that we set this height only once (then 100% works), but the layout model gets several extra visual boxes depending on each other, making the visual structure more complicated and less predictable when it will require reflow/repaint etc. Another approach has the disadvantage that we have to set the header height twice (although we can set only line-height for the main container of inline-blocks, actually), but the rendering tree stays significantly simpler, IMO.

  3. I also thought about setting the line height as 8em but it hit me when you went into multi-line text! Thanks for being so thorough but I would have thought there was an easier way ๐Ÿ˜›

  4. Mary Lou, thanks for the great tutorial!

    That same header with position: fixed; doesn’t expand to the full width of the page. Can you tell me how to position-fix the header, without losing its width?

    Beijinhos ๐Ÿ˜‰

    • @Carlos, if it’s position: fixed you’ll also need width: 100% I would think.

    • Use left: 0; right: 0; instead of width: 100%;. width: 100%; will cause the element to spill out of view if any padding is applied to it (which is fixable with box-sizing: border-box but isn’t very backwards compatible).

    • Thanks a lot for your replies.


      position: fixed;
      width: 100%;
      box-sizing: border-box;

      That works! Thanks.

    • Hello Carlos, how did you manage the fact that a fixed position div with a height of 100% will take the whole viewport height ? I’m having a lot of trouble with that.

  5. Thanks for the great post.

    Can this be achieved if what would be the logo in this article was an image?

  6. Can the justify trick also work for logo on left, menu on center and icons on right?

  7. Thanks for the great post.
    But what if we use images logo instead of text?

  8. If only `text-align-last` were better supported, the generated content hack would be unnecessary!

    Setting `text-align-last:justify` (which works in IE 5.5+ and Fx 12+ with -moz prefix) lets you force the last line to justify, even though it doesn’t overflow.

    • Hi Vladislav,
      looks interesting but if you add a long title (or “long” nav element), the h1 and the nav overlap. How can the width of the elements (and also multi-line text) be controlled in your solution? Thanks for the feedback, cheers, ML

  9. Hi @Mary Lou!
    I ‘m a huge fan of your work and ever since I’ve discovered this website, I’m addicted! Thank you for all the awesome tutorials!

    I’ve integrated the Slide Down Box Menu and have a question but the comments are closed for that discussion. Would you be able to help?

    I love the way the subtabs open and animate out. However, in order for it to do so, I must leave a 400 px height and when they’re not open, it doesn’t look so good.

    Is there any way I can have the sub tabs open over the contents of the website with a transparent bg instead? That would be so helpful and not take up extra space. I’m complely new to this, so any help is greatly appreciated =) Thanks.
    See my website, here
    ~Mandy

  10. First of all nice tutorial Mary!

    I want the menu to have the full width but the centerd the same as my .wrap how can I do this?

  11. Hi, Mary. Thanks for the great tutorial!

    I made one small addition, which was to remove the left padding from the first nav link, so that the links center properly when the responsive layout kicks in:


    header nav a:last-child {
    padding-right: 0;
    }

    header nav a:first-child {
    padding-left: 0;
    }

    Thanks!