Add a Notification Top Bar to Your Theme

church bell

Add a notification top bar to your theme without using a plug-in. Learn how to use a little bit of PHP, JavaScript and some CSS to create a top bar that doesn’t need to access the WordPress database (so it’s fast) and also supports multiple languages, so it works with WPML and PolyLang.

We’ll also add a Close/Dismiss button so users can dismiss the notification. We’ll store whether-or-not the top bar has been dismissed in a cookie.

Example top notification bar
An example top bar

It’s a common site requirement and I looked at some Top Bar plugins, but I really wanted something very light, with no database calls. I also wanted full control of where the top bar gets rendered. All the top bars I found use the WordPress action hook wp_body_open. In most instances this is fine, but sometimes it’s not. Maybe some quirky theme means you need to render it underneath a screen component, like a sticky menu.

So I decided to write my own.

infoBefore you start this tutorial, make sure you’re using a custom child theme.

Sort Out the Structure

We’ll build the module using some PHP, a few CSS definitions, and client-side JavaScript (jQuery) to show/hide the bar and handle when the user clicks on the Close button. Because we’re dealing with several files here, we’ll start by creating a new directory inside our custom child theme – a place we can stash all our files/assets without making the child theme’s main directory messy. Create a directory called hw-top-bar, then create three new/empty text files in it, so you have a folder that looks like this:

image-9-5340615
Files for our Top Bar module – starting off

We’ll create a main function that outputs the HTML for the top bar, and we’ll do this without it needing any parameters. This is so we can call the function from anywhere in our code (such as in header.php or maybe page.php), or we can connect it directly to an action handler. So… if we want our module to work like all the other Top Bar plugins out there, we could invoke it like this:

// Render the top bar just inside the document's <body> element.
add_action('wp_body_open', 'hwtb_render_top_bar');

Or if our child theme uses a custom header.php file, we could insert the top bar ourselves by adding the following to our header.php file:

<!DOCTYPE html><?php
// ...
// The bulk of our header.php file
// ...
?><body <?php body_class(); ?>>
<header>
   <!-- Usually a top bar be inserted into here -->
   <ul class="main-menu">
     <li><a href="/">Home</a></li>
     <li><a href="/blog/">Our Blog</a></li>
     <li><a href="/contact/">Contact Us</a></li>
     <li><a href="/about/">About Us</a></li>
   </ul>
<?php

// Render the top bar.
hwtb_render_top_bar();

?>
</header>

We don’t want to mess about putting an options page into the WordPress Admin area of our site, just to set the HTML content of our top bar. We want to be able to do something like the following in our custom child theme’s function.php file:

// Enable the site's top bar
define('HWTB_IS_ENABLED', true);

// HTML for our top bar (different languages)
define('HWTB_MESSAGE', array(
	'_default' => 'Hello World! <a href="#">More Info</a>',
	'fr_FR' => 'Bounjour le Monde! <a href="#">Plus d'infos...</a>',
	'es_ES' => 'Hola Mundo! <a href="#">Más información...</a>',
));

Now we’ve got an idea of how we’re going to lay it out, it’s time to get stuck-in.

Let’s Write some Code – The PHP Bit

In the hw-top-bar folder, edit the file top-bar.php and paste the following into it.

<?php

/**
 * Headwall WP Tutorials Notification Top Bar
 *
 * https://wp-tutorials.tech/add-functionality/add-a-notification-top-bar-to-your-theme/
 *
 * To enable the top bar, include this file from your function.php file.
 *
 * Then set the constants:
 *
 *    const HWTB_IS_ENABLED = true;
 *    const HWTB_MESSAGE = 'Notification Bar <a href="#">More info...</a>';
 *
 * optional...
 *    const HWTB_IS_CLOSE_BUTTON_ENABLED = true;
 *    const HWTB_HIDE_AFTER_FIRST_VIEW = true;
 *
 * Call hwtb_render_top_bar() from somewhere, or connect it to wp_body_open
 * like this:
 *
 *    add_action('wp_body_open', 'hwtb_render_top_bar');
 */

// Block direct access.
defined('WPINC') || die();

function hwtb_enqueue_scripts() {
	if (hwtb_is_top_bar_enabled()) {
		$theme_version = wp_get_theme()->get('Version');
		$base_uri = get_stylesheet_directory_uri();
		$handle = 'hwtopbar';

		wp_enqueue_style(
			$handle,
			$base_uri . '/hw-top-bar/top-bar.css',
			null,
			$theme_version
		);

		if (hwtb_is_close_button_enabled() || hwtb_is_autoclose_button_enabled()) {
			wp_enqueue_script(
				'js-cookie',
				'https://cdn.jsdelivr.net/npm/js-cookie@rc/dist/js.cookie.min.js',
				false, // no dependencies
				'3.0.0'
			);

			wp_enqueue_script(
				$handle,
				$base_uri . '/hw-top-bar/top-bar.js',
				array('js-cookie'),
				$theme_version
			);

			wp_localize_script(
				$handle,
				'hwTopBar',
				array(
					'cookieName' => 'hwtb_is_hidden',
					'isAutoCloseEnabled' => hwtb_is_autoclose_button_enabled(),
					'isCookieEnabled' => !current_user_can('administrator'),
				)
			);
		}
	}
}
add_action('wp_enqueue_scripts', 'hwtb_enqueue_scripts');

/**
 * This is the main function to render the top bar. Usually this should be
 * called by adding the following to your child theme:
 *
 * add_action('wp_body_open', 'hwtb_render_top_bar'); to your code
 *
 */
function hwtb_render_top_bar() {
	$is_top_bar_enabled = hwtb_is_top_bar_enabled();

	if ($is_top_bar_enabled) {
		$top_bar_html = hwtb_get_html();
		$is_close_enabled = hwtb_is_close_button_enabled();
		$is_autohide_enabled = hwtb_is_autoclose_button_enabled();
		$top_bar_classes = apply_filters('hwtb_container_classes', array('hwtb-top-bar-container'));

		$props = '';
		if ($is_close_enabled || $is_autohide_enabled) {
			$props = 'style="display:none;"';
		}

		printf(
			'<div class="%s" %s>',
			esc_attr(implode(' ', $top_bar_classes)),
			$props
		);

		echo '<span class="hwtb-content">';

		if ($is_close_enabled) {
			echo '<a class="hwtb-close-button" href="javascript:void(0);">';
			echo '<i class="fa fa-times-circle" aria-hidden="true"></i>';
			echo '</a>'; //.hwtb-close-button
		}

		echo wp_kses_post($top_bar_html);

		echo '</span>'; // .hwtb-content
		echo '</div>'; // .hwtb-top-bar-container
	}
}

/**
 * Enable the top bar on any given page-load
 * @return bool
 */
function hwtb_is_top_bar_enabled() {
	$is_enabled = (defined('HWTB_IS_ENABLED') && (HWTB_IS_ENABLED === true) && defined('HWTB_MESSAGE') && !empty(HWTB_MESSAGE));
	return apply_filters('hwtb_is_top_bar_enabled', $is_enabled);
}

/**
 * Enable the close button so the user can dismiss the top bar
 * @return bool
 */
function hwtb_is_close_button_enabled() {
	$is_enabled = !defined('HWTB_IS_CLOSE_BUTTON_ENABLED') || (HWTB_IS_CLOSE_BUTTON_ENABLED === true);
	return apply_filters('hwtb_is_close_button_enabled', $is_enabled);
}

/**
 * Set to true to only show the top bar on the first page load.
 * @return bool
 */
function hwtb_is_autoclose_button_enabled() {
	return defined('HWTB_HIDE_AFTER_FIRST_VIEW') && (HWTB_HIDE_AFTER_FIRST_VIEW === true);
}

function hwtb_get_html() {
	$html = null;

	if (defined('HWTB_MESSAGE')) {
		if (is_string(HWTB_MESSAGE)) {
			$html = HWTB_MESSAGE;
		} elseif (is_array(HWTB_MESSAGE) && (count(HWTB_MESSAGE) > 0)) {
			// Get the default fall-back translation.
			if (array_key_exists('_default', HWTB_MESSAGE)) {
				$html = HWTB_MESSAGE['_default'];
			}

			// Look for a more specific translated HTML message.
			$locale = get_locale();
			if (empty($locale)) {
				// No language set?!
			} elseif (!array_key_exists($locale, HWTB_MESSAGE)) {
				// No message found for our language.
			} else {
				// We found a language-specific message, so let's use it.
				$html = HWTB_MESSAGE[$locale];
			}
		} else {
			// Unsupported data in HWTB_MESSAGE.
		}
	}

	if (empty($html)) {
		$html = 'HWTB_MESSAGE should be a string or an array.';
	}

	return $html;
}

There’s not really very much in here. Essentially, we hook WordPress’ main init action handler so we can do some set-up. Then there’s just the main hwtb_render_top_bar( ) function that we can either connect to an action, or call directly from wherever we want.

Activate the Module

To activate the top bar, edit your custom child theme’s function.php file and paste the following into it.

// Comment-out this line (or set it to false) to disable the Top Bar.
const HWTB_IS_ENABLED = true;
const HWTB_MESSAGE = 'Notification Bar <a href="#">More info...</a>';
const HWTB_IS_CLOSE_BUTTON_ENABLED = true;

const HWTB_HIDE_AFTER_FIRST_VIEW = false; // << NEW


/*
 * Render the Top Bar straight after the <body> element.
 * If you want more control of where the Top Bar is rendered, comment
 * this out and call hwtb_render_top_bar() from somewhere in your
 * theme - probably in your header.php file. Or maybe just connect it
 * to a different action hook.
 */
add_action('wp_body_open', 'hwtb_render_top_bar');

// Include the Headwall Top Bar module.
require_once dirname(__FILE__) . '/hw-top-bar/top-bar.php';

Some of the Nice Bits

When we render the HTML, it’s really just a <div> element with some HTML in it – nothing complicated. But… on some sites we might want to add some additional CSS classes to the <div> container to make it easier to customise. So we added our own ad-hoc filter in there:

$top_bar_classes = apply_filters('hwtb_container_classes', array('hwtb-top-bar-container'));

This means that we can add something in our main functions.php to add some custom CSS classes to our top bar’s div element:

// This code could go in to your custom child theme's function.php file,
// if you want to add to the top bar's CSS classes.
function custom_top_bar_classes($classes) {
	// $classes is just an array of CSS class names.
	array_push($classes, 'custom-top-bar-class');
	return $classes;
}
add_filter('hwtb_container_classes', 'custom_top_bar_classes');

We can also use the hwtb_is_top_bar_enabled and hwtb_is_close_button_enabled filters to override whether or not the top bar is enabled, and whether or not the close button is enabled.

Although there are some other neat things in here, that’ll do for the PHP stuff for now.

Adding Some Style

IN our module, we want to make some simple styles that are generic-looking and will work everywhere. In the hw-top-bar folder, edit the file top-bar.css, paste the following into it and then save.

/**
 * Headwall WP Tutorials Notification Top Bar
 * 
 * https://wp-tutorials.tech/add-functionality/add-a-notification-top-bar-to-your-theme/
 */

.hwtb-top-bar-container {
	background-color: #ff00ff;
	color: white;
	text-align: center;
	position: relative;
	padding: 0.5em 2em 0.5em 2em;
}

.hwtb-content {
	line-height: 1.5em;
}

.hwtb-top-bar-container a.hwtb-button {
	background-color: rgba(255, 255, 255, 0.40);
	color: white;
	text-shadow: 0 0 0.125em black;
	font-weight: bolder;
	padding: 0.25em 0.5em 0.25em 0.5em;
	transition: 0.3s;
	margin: 0 1em 0 1em;
	border-radius: 0.25em;
}

.hwtb-top-bar-container a.hwtb-button:hover {
	text-decoration: none;
	box-shadow: 0 0 0.5em white, 0 0 0.5em white;
}

.hwtb-close-button {
	position: absolute;
	background-color: transparent;
	border: none;
	color: white;
	opacity: 0.80;
	font-size: 150%;
	right: 0.5em;
	top: 50%;
	transform: translateY(-50%);
	font-weight: normal;
	transition: 0.3s;
}

.hwtb-close-button:hover {
	color: white;
	opacity: 1;
}

Customising the Top Bar is easy, but don’t change anything in top-bar.css, because we’ll want to use this file in other WordPress projects. Instead, if you want to change the colour of the top bar, edit style.css in your custom child theme and do something like this:

/* Override key properties of our Top Bar */
.hwtb-top-bar-container {
	background-color:  blue;
}

Styling the top bar is the easy bit 🙂

Finally – Coding the JavaScript Bit

Now let’s do the stuff to handle closing/dismissing the top bar, and storing that preference in a cookie. In your custom child theme’s hw-top-bar folder, open the file top-bar.js and paste the following into it.

/**
 * Headwall WP Tutorials Notification Top Bar
 * 
 * https://wp-tutorials.tech/add-functionality/add-a-notification-top-bar-to-your-theme/
 */

(function($) {
	'use strict';

	// Diagnostics
	// console.log('Top Bar : init');

	$(window).on('load', function() {
		// Diagnostics
		// console.log('Top Bar : load');

		let topBarContainer = $('.hwtb-top-bar-container');
		let closeButton = $('a.hwtb-close-button');
		let shouldBeVisible = (topBarContainer.length > 0);
		let cookieName = hwTopBar.cookieName;

		hwTopBar.markAsDismissed = () => {
			if (hwTopBar.isCookieEnabled) {
				// 7 day expiration.
				let cookieProps = { expires: 7, sameSite: 'strict' };
				let cookieValue = '1';

				Cookies.set(cookieName, cookieValue, cookieProps);
			}
		};

		if ((closeButton.length > 0) || hwTopBar.isAutoCloseEnabled) {
			$(closeButton).click(function() {
				$(topBarContainer).slideUp();
				hwTopBar.markAsDismissed();
			});

			let hasBeenClosed = hwTopBar.isCookieEnabled && (Cookies.get(cookieName) === '1');

			if (hasBeenClosed) {
				shouldBeVisible = false;
			}

			if (!hasBeenClosed && hwTopBar.isAutoCloseEnabled && hwTopBar.isCookieEnabled) {
				hwTopBar.markAsDismissed();
			}
		}

		if (shouldBeVisible) {
			$(topBarContainer).slideDown();
		}

	});
})(jQuery);

We won’t go too much into this because JavaScript and jQuery are a beast in their own right, but here’s the skinny on what’s going on:

After the document’s loaded…

  • We check to see if there are any elements with the class hwtb-top-bar-container.
    • If there are, then there is a top bar on the page.
  • We check to see if there are any link elements with the class hwtb-close-button.
    • If there are, then there is a top bar close button on the page.
  • Back in the PHP file, we checked to see if the current user is an administrator.
    • If so, we used wp_localize_script() to pass some data into the hwtbState object.
    • This means that if we’re logged-in to the site as an administrator, we can’t make the Top Bar go away. It’s useful for reminding us that the top bar is enabled… so we don’t forget :S
  • We check to see if the user has previously dismissed the top bar (if cookies are enabled).
    • If they haven’t disabled it then we call slideDown() on the top bar so that it becomes visible.
  • Finally, we connect the click() method of the close button so that it hides the top bar with a call to slideUp() and stores the number “1” in our “hwtb_is_hidden” cookie… so we can avoid calling slideDown() next time we load a page.

Final Tests

Now we’ve got all three files, you should be able to reload your browser (don’t forget to clear/bypass the cache) and… Boom! You should see your shiny new top bar.

Example top notification bar
The finished top bar

Additional Languages

A cool little feature we added was support for multiple languages. We do this in top-bar.php, in the function hwtb_get_html(). What we do is check the HWTB_MESSAGE constant. If it’s a string, then we assume that the string is some HTML and we just use it. However, if it’s an array, we assume it’s structured like thiis:

define('HWTB_MESSAGE', array(
	'_default' => 'Hello World! <a href="#">More Info</a>',
	'fr_FR' => 'Bounjour le Monde! <a href="#">Plus d\'infos...</a>',
	'es_ES' => 'Hola Mundo! <a href="#">Más información...</a>',
));

By putting the languages in here, as raw strings in PHP, we’re avoid slow/expensive calls to the database. It keeps everything clean and simple.

Wrapping it Up

That’s about it. We’ve got three little files in a self-contained directory, so we can drop the module into any of our future WordPress projects. We can override the top bar’s behaviour by hooking a couple of filters in our functions.php file. We can override the top bar’s appearance in our style.css file.

…and we’ve had a negligible effect on our site’s performance because there aren’t any database calls.

Now that’s what I call tidy 🙂

Like This Tutorial?

Let us know

WordPress plugins for developers

Leave a comment