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

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:

Manoela Ilic

Manoela is the main tinkerer at Codrops. With a background in coding and passion for all things design, she creates web experiments and keeps frontend professionals informed about the latest trends.

Stay up to date with the latest web design and development news and relevant updates from Codrops.

Feedback 75

Comments are closed.
  1. I have been trying this technique for a while yesterday (thanks a lot Mary Lou) but i have a problem: for my design i need a fixed header and when you give a position:fixed property to a header (or let’s say a div) and a height of 100% the header will take the height of the whole window (which actually make sense since the header is out of the document flow). Is there any way to avoid that? I have been trying to give my header a specific height but then the vertical align won’t work. Any help will be REALLY helpful.

  2. I need a fixed header with a 100% height (so i can make the VAlign work properly). Since the Header is out of the document flow the 100% Height is making the header take whe whole viewport height. Is there any way to make this method work with a fixed header with a 100% height but avoiding the header to take the 100% height of the viewport ?

  3. Sometimes you can’t beat a couple of tables and valign=”middle” for simplicity. Yeah it’s depecrated or whatever, but so what? CSS totally failed on this one.

  4. Mary

    This is exactly what I’ve been trying to get to work but using a .png instead of the text.

    Got it doing EXACTLY what I wanted.

    Maximum kudos!!