The WordPress Theme Header Template

Now we get into the nitty-gritty: building up your header.php and validating your theme with an HTML Doctype. There’ll be a lot of PHP in this lesson, but don’t despair. We’re also going to do two essential (and kinda neat) search engine optimization techniques and add some more things to your functions.php file.

This lesson assumes that you have already added the basic HTML structural elements to your header.php file, which we covered in WordPress Theme Template and Directory Structure. If your header.php file is empty, please work through that lesson first, and then come right back.

The Head Section

Right now your very blank WordPress Theme is technically invalid. That’s because it’s missing a Doctype telling the browser how to interpret the HTML it’s seeing. We’re going to use the HTML5 Doctype. HTML5 has seen enough adoption that now is a great time to use it in a WordPress Theme.

Open up header.php and paste the following code there, before anything else.

<?php
/**
 * The Header for our theme.
 *
 * Displays all of the <head> section and everything up till <div id="main">
 *
 * @package Shape
 * @since Shape 2.0
 */
?><!DOCTYPE html>

We’re going to add an opening HTML tag with some attributes that will make the type of page we’re creating more apparent to the browser. At the same time, if the current browser is Internet Explorer 8, the opening HTML tag will have an ID of “ie8”. This allows us to create targeted CSS styles later on, just for IE 8, without having to create a separate stylesheet. Go ahead and add the following, below the Doctype.

<!--[if IE 8]>
<html id="ie8" <?php language_attributes(); ?>>
<![endif]-->
<!--[if !(IE 8) ]><!-->
<html <?php language_attributes(); ?>>
<!--<![endif]-->

The opening HTML tag is wrapped in HTML conditional comments. If the browser is IE8, the HTML tag gets the “ie8” ID. If the browser is not IE8, don’t include the “ie8” ID. Want to target more IE versions? Just add more conditional comments.

Now we’re going to get into the <head> section of your WordPress Theme. The <head> section contains meta-information about a web page. Typically stuff like the document title seen at the top of your browser (and in search engine results), and links to stylesheets and RSS feeds. Place the following code below the last code you added.

<head>
<meta charset="<?php bloginfo( 'charset' ); ?>" />
<meta name="viewport" content="width=device-width" />
<title><?php
/*
* Print the <title> tag based on what is being viewed.
*/
global $page, $paged;

wp_title( '|', true, 'right' );

// Add the blog name.
bloginfo( 'name' );

// Add the blog description for the home/front page.
$site_description = get_bloginfo( 'description', 'display' );
if ( $site_description && ( is_home() || is_front_page() ) )
echo " | $site_description";

// Add a page number if necessary:
if ( $paged >= 2 || $page >= 2 )
echo ' | ' . sprintf( __( 'Page %s', 'shape' ), max( $paged, $page ) );

?></title>
<link rel="profile" href="http://gmpg.org/xfn/11" />
<link rel="pingback" href="<?php bloginfo( 'pingback_url' ); ?>" />
<!--[if lt IE 9]>
<script src="<?php echo get_template_directory_uri(); ?>/js/html5.js" type="text/javascript"></script>
<![endif]-->
<?php wp_head(); ?>
</head>

<body <?php body_class(); ?>>

If all this looks like gobbledygook to you that’s OK. I’ll explain the main sections.

<meta charset="<?php bloginfo( 'charset' ); ?>" />
<meta name="viewport" content="width=device-width" />

This is meta information about our content. The first line displays the character set your blog is using. The second line sets the width of the viewport to the width of the device you’re using (for example, a desktop computer, iPhone, iPad, etc).

<title><?php
/*
* Print the <title> tag based on what is being viewed.
*/
global $page, $paged;

wp_title( '|', true, 'right' );

// Add the blog name.
bloginfo( 'name' );

// Add the blog description for the home/front page.
$site_description = get_bloginfo( 'description', 'display' );
if ( $site_description && ( is_home() || is_front_page() ) )
echo " | $site_description";

// Add a page number if necessary:
if ( $paged >= 2 || $page >= 2 )
echo ' | ' . sprintf( __( 'Page %s', 'shape' ), max( $paged, $page ) );

?></title>

That’s the <title> tag, which displays the site title at the top of the browser window. How this title appears depends on which page is being viewed. Let’s break it down into sections.

For every page in your theme except the front page, we want to show the current page’s title immediately followed by a pipe separator on the right. The WordPress function wp_title() does this work for us. Immediately to the right of the pipe separator, we want to include the blog name. The handy bloginfo() function makes this possible.

We’re going to display the title a bit differently on the front page: the site title, a separator pipe, and the site description.

First we declare a variable called $site_description to store the site description so that we can use it later. In PHP, variables start with a “$”. We set $site_description equal to the value that get_bloginfo( 'description', 'display' ); returns. Notice the semi-colon (“;”) at the end. Whenever you declare a variable in PHP, be sure to end it with a semi-colon.

Next, we have a simple conditional statement that, in plain English, reads: “if the site has a site description and this is either the home page or a static front page, display the site description.”

Finally, we add page numbers on “older post” pages.

<link rel="profile" href="http://gmpg.org/xfn/11" />
<link rel="pingback" href="<?php bloginfo( 'pingback_url' ); ?>" />

The first line adds support for XFN, and the second line provides links for our pingbacks.

<!--[if lt IE 9]>
<script src="<?php echo get_template_directory_uri(); ?>/js/html5.js" type="text/javascript"></script>
<![endif]-->

Look, more HTML conditionals! This part loads a JavaScript file that enables old versions of Internet Explorer to recognize the new HTML5 elements, and it only loads for visitors who are using IE 8 or earlier.

Adding this part is optional. If you’d like to use the plugin, let’s set it up now. You should already have a “js” folder in your theme directory. Navigate to your js folder, and create a new file called “html5.js”. Here’s the plugin code from _s. Paste all of it into your html5.js file.

Next, we have:

<?php wp_head(); ?>

This is a required hook. WordPress plugins and other cool things rely on it.

<body <?php body_class(); ?>>

The body tag for our theme. The body_class() function adds a bunch of useful CSS classes to the body tag that come in handy when we want to style the theme based on a variety of conditions. To see a live example, view the source of this page, and look for the body tag. You’ll see something like this:

<body class="single single-post postid-3781 single-format-standard singular">

Because we’re on a single post, we get the class “single”. Because this post is of the standard post format, we get the “post-format-standard” class. View the source of different types of views (archives, pages, the front page, search results, etc) and observe how the body classes change.

The header section

Now we want to add in our site title, which will act as a link to our home page, site description, and menu.

In header.php move down to the hgroup tags. It’s here that we’ll add in the title and description. Replace the opening and closing hgroup tags with this:

<hgroup>
     <h1 class="site-title"><a href="<?php echo home_url( '/' ); ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home"><?php bloginfo( 'name' ); ?></a></h1>
     <h2 class="site-description"><?php bloginfo( 'description' ); ?></h2>
</hgroup>

In the code above we’re using a WordPress Template Tag called home_url(). You can see we’re using it to get the main URL of our WordPress blog.

To get the name of our blog, and the description, we’re using bloginfo(). You’ll recall that we used this function earlier when printing our title tag (also notice the escaping with esc_attr() on the “title” attribute of the site title link). It can be used to get over 20 different pieces of information about your blog. It takes that information and prints it in your template. Understand this and you understand WordPress Themes. We take an HTML structure and we call WordPress Template Tags with PHP to fill it out. Simple stuff really. It’s just a matter of getting used to seeing the template tags in there, along with some IF statements and a few PHP loops (we’ll get to those too). Move on down to the nav section. We’re going to add a skip link so folks using a screen reader don’t have to suffer through our menu when they just want to get to the content. Right after <nav>, add the following:

     <h1 class="assistive-text"><?php _e( 'Menu', 'shape' ); ?></h1>
     <div class="assistive-text skip-link"><a href="#content" title="<?php esc_attr_e( 'Skip to content', '_s' ); ?>"><?php _e( 'Skip to content', 'shape' ); ?></a></div>

and we’re going to add the custom menu that we created in Setting Up Your Theme Functions, really couldn’t be any easier as it’s just one template tag, with only 1 argument:

<?php wp_nav_menu( array( 'theme_location' => 'primary' ) ); ?>

Easy, right? So your <nav> section should look like this:

<nav role="navigation" class="site-navigation main-navigation">
     <h1 class="assistive-text"><?php _e( 'Menu', 'shape' ); ?></h1>
     <div class="assistive-text skip-link"><a href="#content" title="<?php esc_attr_e( 'Skip to content', 'shape' ); ?>"><?php _e( 'Skip to content', 'shape' ); ?></a></div>
     <?php wp_nav_menu( array( 'theme_location' => 'primary' ) ); ?>
</nav><!-- .site-navigation .main-navigation -->

If you created multiple nav menus in functions.php, you can add them in the same manner as we have here, in the location where you want your menu to appear (and be sure to change “primary” in ‘theme_location’ => ‘primary’ to match the name of the additional menu).

We’ve got a few more things to take care of, and then we’ll be done. We’re going to get functions.php out again and add a new function to load our stylesheet and a few other JavaScript-powered goodies. Open functions.php and add the following at the end of the file.

/**
 * Enqueue scripts and styles
 */
function shape_scripts() {
	wp_enqueue_style( 'style', get_stylesheet_uri() );

	if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
		wp_enqueue_script( 'comment-reply' );
	}

	wp_enqueue_script( 'navigation', get_template_directory_uri() . '/js/navigation.js', array(), '20120206', true );

	if ( is_singular() && wp_attachment_is_image() ) {
		wp_enqueue_script( 'keyboard-image-navigation', get_template_directory_uri() . '/js/keyboard-image-navigation.js', array( 'jquery' ), '20120202' );
	}
}
add_action( 'wp_enqueue_scripts', 'shape_scripts' );

As you can see, we’re using wp_enqueue_style() and wp_enqueue_scripts() to load our stylesheet and JavaScript files, respectively. It’s the current best practice to use these functions to load CSS and JavaScript into themes and plugins, instead of hard-coding links into header.php.

At the end of the function, we hook shape_scripts() onto wp_enqueue_scripts(), which will dynamically add links to your stylesheet and scripts into the header.

Let’s look at the rest of the function.

	if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
		wp_enqueue_script( 'comment-reply' );
	}

Here, we’re loading the comment-reply.js script (which comes bundled with WordPress) that moves the comment form underneath the comment you’re replying to when you click the “reply” link. Notice how we’re only loading comment-reply.js if we’re on a single post or page, comments are open, and threaded comments are enabled. There’s no need to waste resources loading this script in places it’s not needed.

	wp_enqueue_script( 'navigation', get_template_directory_uri() . '/js/navigation.js', array(), '20120206', true );

	if ( is_singular() && wp_attachment_is_image() ) {
		wp_enqueue_script( 'keyboard-image-navigation', get_template_directory_uri() . '/js/keyboard-image-navigation.js', array( 'jquery' ), '20120202' );
	}

Here are two optional, but neat scripts (if you don’t want to use them, just delete them from the function). The first, navigation.js, turns your main navigation menu into a nifty mobile-friendly menu on smaller screens. The second, key-board-image-navigation.js, adds the ability to navigate through images using the left and right arrow keys on your keyboard. This script only loads on single, image attachment pages.

The second script requires jQuery. Notice how we pass “jquery” as a parameter within the wp_enqueue_script() function. This tells WordPress that the script depends on jQuery. As a result, WordPress will also load the jQuery library. We don’t have to include the actual library in our theme files, because WordPress already comes bundled with multiple JavaScript libraries and default scripts. If you find other cool scripts to add to your theme in the future, just come back and add them to shape_scripts, in the same way we’ve loaded these scripts. Pretty cool, huh?

We’re almost done! If you opt to use navigation.js and keyboard-image-navigation.js, you need to add them. Go ahead and create the two files in your theme’s js folder. Here is the respective code to paste into each, grabbed from _s: navigation.js and keyboard-image-navigation.js.

And that’s it! Your WordPress Theme Header Template is search engine optimized and coded up, and your stylesheet and you’ve got a nice, clean function to load your stylesheet and other theme scripts.

How To Create a WordPress Theme

This post is part of the The ThemeShaper WordPress Theme Tutorial: 2nd Edition that will show you how to create a powerful WordPress Theme from scratch. Read it from the beginning and code yourself up something awesome.

  1. WordPress Theme Tutorial Introduction
  2. Developing Theme Sense
  3. Theme Development Tools
  4. Creating a Theme HTML Structure
  5. Template and Directory Structure
  6. Setting Up Your Theme Functions
  7. Secure Your WordPress Theme
  8. The Header Template
  9. The Index Template
  10. The Single Post, Post Attachment, & 404 Templates
  11. The Comments Template
  12. The Search Template & The Page Template
  13. The Archive Template
  14. The Sidebar Template & The Footer Template
  15. Reset-Rebuild Theme CSS & Define Your Layouts
  16. Custom Background & Custom Header
  17. Distributing Your WordPress Theme

43 responses

  1. I think I may be missing something here

    Replace the current tag in header.php
    and
    Replace

    Where and when were these added?

    1. Hi Ian! It looks like your code was stripped out of the comments, but I’m assuming you’re asking when we added the code to replace? It was added in the WordPress Theme Template and Directory Structure lesson.

      I’ve updated this lesson to make this more clear.

      1. I noticed this too. You were saying to replace the html and header tags but they were not part of the WordPress Theme Template and Directory Structure lesson.

      2. Aha, I see what you mean now. Sorry for the confusion, I’ve edited the header lesson to remove the instruction to “replace” those tags.

  2. […] Home › Themes › The WordPress Theme Header Template […]

  3. these were added in a previous article, off hand not sure which one (4?).

    as well, don’t forget the body tag in header.php, looks like it was skipped

    Al

    1. Yes, it looks like I omitted it. I’ve updated the lesson to include the opening body tag. Thank you very much for pointing that out. 🙂

  4. […] Right now your very blank WordPress Theme is technically invalid. That’s because it’s missing a Doctype telling the browser how to interpret the HTML it’s seeing. We’re going to use the HTML5 Doctype. HTML5 has seen enough adoption that …More By Ian Stewart […]

  5. Just a note about the title tag: it should only contain wp_title and nothing else, so plugins can modify it easily. To customize the output in the theme, one should use the wp_title filter instead.

    Yoast talks a bit about it here.

  6. I’m loving the series guys. I wish I had this series when I first started learning theming! I will definitely be linking loads of people to this series. I’ll have to make sure Krista or Rebecca link to this from the Code Poet site once the series is done too 🙂

    I think it’s worth a mention of what we did in Twenty Twelve this year in the title tags.

    then in functions.php we added filtered that with the twentytwelve_wp_title function which in this case would go in your template-tags.php. Oh and by 'we' I mean everyone else who contributed this part. I only added some easy CSS patches ;) When Twenty Thirteen starts work I'll do some php too :D

    1. Looks like I didn’t add my code tags properly above cause there should’be been a

      wp_title( '|', true, 'right' );

      In my above comment

  7. The post on the Index template is missing. It was there previously, but now it is gone.

    1. Hi Ron! The Index Template lesson has been restored. We were experiencing some technical difficulties earlier. I apologize for the inconvenience!

      1. Thanks. I know how those things happen.

  8. You tell us to open ‘header.php anf make changes to I have looke for this tag and in fact I had the browser run a “find” for this tag and neither one of us can find it. Therwe are no tags in header.php. at least not yet maybe you’re planning on having us add those later.. Actyally I ran tweh ‘find” routine on this web page where teh head section and presumably all the other code for header.php thus far is to be found and “run can’t find hgroup anywhere except where you tell us to make these changes.

    So I’m wondering what I’m missing and how did I misss it?

    1. Hi Ron, as mentioned at the beginning of the article, the Header Template lesson assumes you have completed the WordPress Theme Template Directory Structure lesson. Did you complete that one?

  9. I’m getting the following error:

    Parse error: syntax error, unexpected ‘wp_title’ (T_STRING) in D:\Websites\Xampp\htdocs\blogmylunch\wp-content\themes\shape\header.php on line 29

    Line 29 is:
    wp_title( ‘|’, true, ‘right’ );

    1. Can you copy/paste your line 28 as well? You might be missing a semicolon at the end of line 28 because wp_title( '|', true, 'right' ); shouldn’t return an error

  10. shawnmathew Avatar
    shawnmathew

    I’ve followed the code exactly but everything that should be under the element is under the tag for some reason.

    I also keep getting these errors:
    SCREAM: Error suppression ignored for

    ( ! ) Warning: call_user_func_array() expects parameter 1 to be a valid callback, function ‘Shape’ not found or invalid function name in C:\wamp\www\Wordpress\wordpress-3.6\wordpress\wp-includes\plugin.php on line 406

    No clue what I’m doing wrong..

    1. Hi Shawn, you could try downloading the entire working theme here to check out what might be going awry in your version:
      https://themeshaper.files.wordpress.com/2013/10/shape.zip

      Good luck!

  11. This tutorial is fantastic! Easiest to follow, comprehensive, and fun 🙂 Thank you!

  12. The menu doesn’t seem to work in Chrome for OSX (Version 30.0.1599.69). I can’t get it to appear if it is within the nav tag. I tried with the original Shape theme as well and the same issue appears. Anyone else?

  13. Excellent tut guys I’m finding really helpful. One comment, is it not better to place js files before the closing body tag? Can this be done using enque_script? Thanks again!

    1. Hi Nick, the JavaScript file being called in the header – html5.js – is wrapped in a conditional that only calls it in if the browser is older than IE9, so it won’t actually be loaded on any other browser. This is a special script that needs to be loaded in the header and is an exception to the usual guidelines about loading JavaScript before the closing body tag.

      Hope this helps answer your question and glad you’re enjoying the tutorial.

  14. Parse error: syntax error, unexpected ‘wp_title’ (T_STRING) in E:\XAMPP\htdocs\wordpress\wp-content\themes\GREGS\header.php on line 27

    I am getting this error! wp_title(‘|’, true,’right’);

  15. I have written like this but now its working!! How come?

  16. Changing the nav tag to a div tag seems to correct the issue. I just don’t know why.

  17. Just a note: in the final working theme (downloaded from: https://themeshaper.files.wordpress.com/2013/10/shape.zip ) we got small-menu.js instead of navigation.js. 🙂

  18. I’m getting a 404 Page Not Found for the keyboard-image-navigation.js

  19. hello the archive keyboard-image-navigation.js. is missing, where i can find it?? thank you for this great tutorial!

  20. Thanks for the notes about the broken JS file link, it’s fixed now.

  21. Loving the guide! Quick question though: at the end of this part, should I be able to activate the theme and see the title, etc? All I’m getting is the white screen of death (and dashboard disappears, etc), but can’t see anything I’ve missed…

    1. Hi Chris – you’ll need more than just header.php to view anything your browser, as the page hasn’t finished rendering yet in only that file. 🙂

  22. There’s a typo where you explain The Head Section. Search for “Now we’re going to get into the get into the section of your”

    I am loving this tutorial thoroughly. A Sunday well spent.

    1. Great catch, Husain, thank you! I’ve made the fix. Glad you’re enjoying the tutorial!

  23. Right now your very blank WordPress Theme is technically invalid. That’s because it’s missing a Doctype telling the browser how to interpret the HTML it’s seeing. We’re going to use the HTML5 Doctype. HTML5 has seen enough adoption that now is a great time to use it in a WordPress Theme.

    1. Hi Desain, the HTML 5 Doctype is simply:

      Here’s the reference at W3C.

      In other words, , case-insensitively.

      This is included in the header template file, on line 10 of header.php.

      If you meant something else, just let me know. Thanks!

  24. Maybe it’s worth mentioning that the navigation.js call isn’t included yet because it will be loaded in the footer and therefore [i]wp_footer()[/i] has to be placed into footer.php.
    Took me quiet some time to figure out why this call isn’t made!
    😉

    1. Hi MegaWork, could you clarify a bit what you mean? navigation.js is loaded via wp_enqueue_scripts(), which is discussed in the Header Template lesson.

  25. Everything great with this tutorial until this point (or perhaps the last step), I am getting this message:

    Fatal error: Cannot redeclare shape_page_menu_args() (previously declared in C:\XAMPP\htdocs\wordpress\wp-content\themes\shape\inc\template-tags.php:16) in C:\XAMPP\htdocs\wordpress\wp-content\themes\shape\inc\tweaks.php on line 19

    Any idea what this is about? I did activate the theme on my WordPress install on localhost, did I make a mistake here with the theme being unfinished (or is the point of it being installed locally that I can view what the output is as I go along?)

    Many thanks for any help you can offer

    1. If the theme isn’t finished you will likely get errors trying to use it on an install, whether it’s local or not. If your theme is finished, try downloading the final version from the sidebar, where it says “Download the finished Shape theme,” and compare it carefully to your version to find the glitch. Good luck!

  26. I’m new with wordpress btw. I wonder why we call css with function? I downloaded the “finished Shape theme” and css doesnt load. It say that css is loaded from function, I’m not sure why header.php has called wp_enqueue_scripts or shape_scripts function.

    1. Welcome! There are some good reasons for enqueuing CSS with a function. One is that it makes things like minifying CSS work better, and that’s a more efficient way to serve CSS from the server. This page is related and has a bit more info in case you’d like to check it out: http://developer.wordpress.com/themes/enqueues/