Getting Sticky Headers and the WP Admin Bar to Behave
Putting a site’s logo and main navigation into a sticky header (or a fixed-position header) is a definite trend now. It’s become popular for WordPress themes as well: check out posts like all of these.
In a WordPress site, however, sticky headers run into a problem when the admin bar is visible. Both the admin bar and most sticky headers rely on position: fixed; top: 0;
, and are positioned according to the viewport. They end up overlapping. Since the admin bar has a z-index
of 99999
, it usually covers a good bit of the theme’s sticky header (or if the header wins the z-index
war, then it covers the admin bar). Let’s look at how we can use CSS (and Sass) to fix this problem.
[Quick note: some themes use JavaScript to position sticky elements as the page scrolls. If JavaScript is constantly updating an inline top
property, the technique outlined below won’t fix this problem.]
CSS to Move the Header
For the sake of simplicity, we’ll use the class .sticky-header
to refer to the element that’s fixed to the top of the page. You should find the correct selector for your theme and use that. We’ll also assume that its top
position value is 0. If it’s top
is offset to anything else, you’ll need to do the math and tweak the measurements below.
When the admin bar is visible on the front-end, WordPress attaches the class .admin-bar
to the <body>
of the page. (This is handled by the function body_class();
in header.php
most of the time.) That class will let us adjust the top
position of our sticky header.
/* This is the existing CSS... */
.sticky-header {
position: fixed;
top: 0;
}
/* Here's the new CSS to add... */
.admin-bar .sticky-header {
top: 32px;
}
That’s pretty manageable, right? The admin bar is 32px tall, so we just move the sticky header down and both are visible. The only problem is, it’s not always quite that simple: the admin bar isn’t always 32px tall. In which case, you’ll need to use CSS.
CSS for Small Screens
On screens narrower than 783px, the admin bar is 46px tall. We’ll need to modify our code to compensate for that:
.admin-bar .sticky-header {
top: 32px;
}
@media screen and (max-width: 782px) {
.admin-bar .sticky-header {
top: 46px;
}
}
If you prefer mobile-first CSS (as I do), use this code:
.admin-bar .sticky-header {
top: 46px;
}
@media screen and (min-width: 783px) {
.admin-bar .sticky-header {
top: 32px;
}
}
Now you’ll have the sticky header positioned correctly, no matter how wide the screen is! We’re not quite through, though. If you use Sass to build your theme, we can roll this up into a reusable mixin.
If you’re a theme developer or building a child theme, you already know where to put this CSS in your existing stylesheets. If, on the other hand, you’re working with an existing theme that doesn’t handle its sticky header very well and you simply need to insert this code, you can use a plugin that allows you to insert CSS like WP Add Custom CSS or Simple Custom CSS.
Sass to Move the Header
I’m going to call this mixin admin-sticky-fix()
. We’ll take a look at the entire block of code, then look at how each part works.
@mixin admin-sticky-fix( $offset: 0 ) {
$narrow-offset: 46px;
$wide-offset: 32px;
@if $offset != 0 and type-of($offset) == 'number' {
$narrow-offset: $narrow-offset + $offset;
$wide-offset: $wide-offset + $offset;
}
.admin-bar & {
top: $narrow-offset;
@media screen and (min-width: 783px) {
top: $wide-offset;
}
}
}
The mixin takes an optional parameter: $offset
. This allows you specify that the element has a top
value other than 0
. If you don’t specify any offset, the mixin will assume 0
and work with that. If you manually specify an $offset
, the @if
condition will modify the default admin bar height values to match. Note: Just use an integer: the px
unit isn’t needed, but it won’t break things if you do include it.
Our mixin uses Sass nesting to simplify things: the &
represents whatever selector this mixin is called inside of. So if you @include
it in .sticky-header
, you’ll get the output selector .admin-bar .sticky-header
. You’ll notice the media query is nested inside the selector as well. Sass automatically moves the @media [etc]
back to root and copies the current selector into it. This nesting trick just lets us write a shorter mixin.
Let’s see how this mixin works now:
/* Sass we write */
.sticky-header {
position: fixed;
top: 0;
@include admin-sticky-fix;
}
/* CSS Output */
.sticky-header {
position: fixed;
top: 0;
}
.admin-bar .sticky-header {
top: 46px;
}
@media screen and (min-width: 783px) {
.admin-bar .sticky-header {
top: 32px;
}
}
If you need to match an offset value, use the mixin like this:
/* Sass we write */
.sticky-header-offset {
position: fixed;
top: 20px;
@include admin-sticky-fix(20);
}
/* CSS Output */
.sticky-header-offset {
position: fixed;
top: 20px;
}
.admin-bar .sticky-header-offset {
top: 66px;
}
@media screen and (min-width: 783px) {
.admin-bar .sticky-header-offset {
top: 52px;
}
}
Having a reusable mixin allows us to quickly tweak any fixed-position item to compensate for WordPress’ admin bar. My mobile menus are often fixed position and this lets me easily adjust their top
value as well.
Conclusion
By default, sticky headers will clash with the WordPress admin bar, but with these code snippets, the fix is quite manageable. This Sassmeister gist contains the mixin and a few examples of how it works. Where have you seen good implementations of the sticky header trend and how do you style itgrate in your WordPress themes?