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.

Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.

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!

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 in the loop: Get your dose of frontend twice a week

Fresh news, inspo, code demos, and UI animationsβ€”zero fluff, all quality. Make your Mondays and Thursdays creative!

Feedback 75

Comments are closed.
  1. As someone who usually just keeps trying stuff until it works with CSS, this is rather useful!

  2. Thanks for the great walkthrough. I always look forwards to your contributions. Btw, very nice new profile pic too!

  3. Very useful article ML, Thank You.
    BTW you look more beautiful with new profil picture πŸ˜‰

    • Obviously this won’t work on IE7 since it doesn’t support inline-block property. I haven’t tested this on IE8 but it should work. Btw Mary, thank you for the trick. This will be really useful.

    • As @Bobby pointed out, `inline-block` is not supported, per se. However, IE <=8 will treat any `display:inline` element like `display:inline-block` if `hasLayout` is triggered. So simply apply `zoom:1` or some similar `hasLayout` trigger and IE will let those inline elements behave just like inline-block.

      However, getting IE <=8 to handle inline-block is only half the problem, since IE <8 doesn't handle generated content. Thus, there's no way to force the left and right elements apart. Or is there?…

      IE 5.5+ supports `text-align-last` so setting that to `justify` will force even a single-line header to justify, even without the generated content hack. (see also my comment pointing this out)

  4. I am not one to comment on sites, actually this might be the first time, but this article saved the day. Sometimes for people at my level (3 years in) I might spend an entire day or more to fix some element that you just explained how to fix. Not sure if requests are ever taken but more article like this…centering, padding on responsive mobile flying invisible devices πŸ™‚ you know what I mean? If someone told me years ago that a container inside a container makes it scalable I would have much more hair on my head. OK You are the best and I read and use everything you put out. keep up the great work. hope some of this made sense

    peace

  5. Great work as always, ML. This will be very useful in a current project. Thanks for sharing.

  6. This is great but I am still gob smacked at how something so simple to achieve can be so frustrating in css. Look at the steps you have to go through just to centre text. Coming from a print layout background who has moved in web i see it as a frustrating waste of time.

    But thanks for the solution.

    • Pinky I also came from a print background years ago, and used to carry the spitefulness towards coding, but once you wrap your head around CSS/HTML you learn to embrace tricks like this. Besides, print has similar little problems that can lead to hours of frustration, that is until you learn the tricks to solve them and move on with a smile πŸ™‚

  7. I’ve been trying to find good ways to center my items in my headers, and this just helped me a lot! And it was simple too πŸ˜€ Thanks Mary!

  8. Great post, thank you Mary Lou! I never would have thought to use a pseudo element approach. I think I personally would have taken the display: table approach for this.

  9. This technique is in most circumstances just senseless. Prefering pseudo-elements to floatings is just more css typing and causes more rendering time and got no benefits at all. Just remove all pseudos, float those elements and check the line-heights. AAAAND that div wrapping the h1 is really really unnecessary.

    This article just shows how to make things more complicated and due to bad browser support of ::after in microsofts IE8….uh you know πŸ™‚
    (IE8 only supports the single-colon CSS 2.1 syntax (i.e. pseudo-class). It does not support the double-colon CSS3 syntax (i.e. pseudo-element))

    Please be carefull with that.

    • Dom, I absolutely disagree with you. If you use floats, you need to clear them, line-heights won’t help you when text breaks and wrapping a div around a H1 is not overkill considering that you only need it if you define a fixed height for the header. This example is as simple as it can get and you can’t tell me that using floats will make this less complicated.

      And as I mention in the article: “If you’d like to support IE8, you should use β€œ:” instead of β€œ::” for the pseudo-elements.” It would be really nice if you read it and not just flew over it and comment πŸ˜‰

      Please make a demo using floats and we can discuss it from there.

      Thanks for your feedback,
      ML

    • Dom, you’re not very experienced with responsive webdesign are you? She just solved a really common problem in a very clean way… :\

  10. Thanks Mary this is one way I would have never approached a situation like this… well until now! It still amazes me how many solutions there are to our everyday web problems.

  11. 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.

  12. 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.

  13. 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 πŸ˜›

  14. 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.

  15. Thanks for the great post.

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

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

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

  18. 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

  19. 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

  20. 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?

  21. 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!

  22. Hi Mary, I loved this tutorial. The code stays clean and beautiful.
    I would only suggest you to use an <hgroup> instead of a <div> for more semantic purposes.

    πŸ˜‰

    • @Habner It was mentioned already, but has been deprecated for a few months now. With all these new tag elements, you should be checking the specs often to make sure they are still valid.

  23. Hi, this is an awesome technique, but I just spent like 2 hours trying to get this to work.

    The problem I had is that you need white space between the interior elements for text-align: justify to work. Not whitespace that you have explicitly defined, but whitespace in the actual markup.

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

    This doesn’t
    <header><div><h1>Super Bad</h1></div><nav><a>First Link</a><a>Second Link</a><a>Third Link</a></nav></header>

    I am using a web framework that concatenates the HTML generated from its templating language onto one line (to save space i guess?). This resulted in this trick not working. While I can change this, it seems really really risky to have the entire layout of an important part of your site depending on the unrelated internals of your server.

    Please warn people in your tutorial, so that nobody else suffers a similar fate.

    • Just a follow up, was able to solve this problem in what I hope is a solid way: by putting an &nbsp; between the elements, I was able to explicitly define the whitespace required.

  24. Hi, I’ve tried this code
    header { background: #2c3e50; color: #fff; padding: 2em 10%; letter-spacing: 1px; height: 8em; text-align: justify; } header h1, header nav { display: inline-block; } header::after, header::before { content: ''; display: inline-block; width: 100%; background: black; }

    And I get justified and vertically centered effect.
    Could this code become alternative?

  25. hi, Mary i love this tutorial. i am still learning web designing ,and there are somethings i would like to ask you . looked for you on Facebook but i couldn’t find you, please how do i contact you thanks .

  26. This is simple but not so simple …
    Many frontends frameworks included an header, but resizing it need to change many css lines.
    Here : everything is verticaly and horizontaly in place and you just need to change 1 value to height it.
    It’s sure now I’m a noob :o) Bravo!
    Note : next step : codrops framework ? Everything is here …

  27. Hi Mary,
    Isn’t it easier to use display: table;? The code would be more simple and clean.