CSS Fix For Sticky Headers with the WordPress Admin Bar

If you’ve developed themes with HTML & CSS for WordPress, you know that the admin bar often interferes with sticky headers.  The elements overlap each other making one illegible. Both elements are usually placed at the very top and depending what z-index value is used, one is going to appear on top of the other.

Let’s assume you are using a mark-up similar to what is below:

<div class="header sticky">
    <!-- YOUR HEADER ELEMENTS -->
</div>

And your CSS for that element could be as simple as:

.header.sticky {
    position:fixed;
    top: 0;
}

Well, that unfortunately renders right where WordPress likes to put the admin bar.   It will render something like the image below where the header and admin bar overlap one another.

Luckily, there is a quick CSS fix which will move the sticky element below the admin bar when it is rendered.  If the WordPress admin bar is being used, the <body>  element gets a class of .admin-bar which makes it incredibly easy to make a rule for.  Since the admin bar is 32px in height on desktop and 46px on mobile, we can simply add:

.header.sticky { 
    position:fixed; 
    top: 0; 
}

body.admin-bar .header.sticky {
    top:32px;
}

@media all and (max-width:782px) {
    body.admin-bar .header-sticky {
        top:46px;
    }
}

This moves your sticky element below the admin bar, if present, and renders the site how you would expect to see when having two position:fixed; elements.

Spacing for Fixed Elements in the Header

You’ll notice on this site that I use a position:relative; element called .header-spacer  to serve as a “margin” for the fixed header.  I like using this trick to account for the correct spacing you’d like to see with a sticky element so the relative elements aren’t pushed to the top of the page.  Below is an example of the mark up on how I prefer to accomplish this:

<div class="header sticky"></div> <!-- MY STICKY HEADER -->
<div class="header-spacer"></div> <!-- THE SPACING FOR MY HEADER -->
<div class="content"></div> <!-- MY MAIN CONTENT -->

And the CSS would be:

.header.sticky {
    position:fixed;
    top:0;
    height:100px; /* THIS WILL VARY */
}

.header-spacer {
    position:relative;
    height:100px; /* THIS WILL VARY */
}

The caveat is you need to know your fixed element’s height.  I have found over the years that it is pretty easy to identify and maintain this, even when styling for responsive and mobile. People often use Javascript to calculate the height of the header and then apply that to the header-spacer, but this can often be inconsistent when there are images involved depending on when they are rendered.

I prefer to just code the fixed element, figure out the height, and hard code it in my CSS.  There are many ways to accomplish what I am doing, but I have found that this is the most seamless when manipulating with Javascript actions on scroll.  When the margin/spacing for the main page content is determined in the header, rather than the content, it tends to be more consistent for development going forward.

So in the event I would like this spacing to remain the same with the admin bar present, I would add this to my CSS:

.header.sticky {
    position:fixed;
    top:0;
    height:100px; /* THIS WILL VARY */
}

.header-spacer {
    position:relative;
    height:100px; /* THIS WILL VARY */
}

body.admin-bar .header.sticky {
    top:32px;
}

body.admin-bar .header-spacer {
    position:relative;
    height:calc(100px + 32px); /* ADD 32px TO HEIGHT FOR ADMIN BAR */
}

@media all and (max-width:782px) {
    body.admin-bar .header-sticky {
        top:46px;
    } 
    body.admin-bar .header-spacer {
        height:calc(100px + 46px); /* ADD 46px TO HEIGHT FOR ADMIN BAR */
    }
}

This means going forward, I will have a 100px spacing that is calculated into my page height and content, whether I have the admin bar present or not.  It will adjust accordingly thanks to my CSS.

Again, there are a myriad of ways to accomplish this same task but I prefer this method.  Hopefully it helps you going forward.  Happy coding!

3 Comments

  • At 600px, the WordPress admin bar is no longer sticky. In my code, I’ve added the following :

    @media all and (max-width: 600px) {
    body.admin-bar .header-sticky {
    top: 0;
    }
    }

  • You may be correct!  I should note that the template needs to use the function

    <?php body_class(); ?>

    in the body tag and then it should render the correct classes for the admin.  Thanks for bringing that to my attention!

  • This “If the WordPress admin bar is being used, the <body>  element gets a class of .adminbar” is not true in our case, and therefore the code doesn’t work.

Leave A Comment

Your email address will not be published. Required fields are marked *