The WordPress Theme Header Template

Update: We’ve created a second edition of this article, with updated code samples and coverage of the latest theme development techniques. Check it out at The WordPress Theme Header Template. It’s part of The ThemeShaper WordPress Theme Tutorial: 2nd Edition.

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 tricks and start tricking out your theme with a functions.php file.

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 XHTML Transitional Doctype. There are other options but right now, XHTML transitional is the best bet for a WordPress Theme.

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

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

We’re also going to add some attributes to your opening HTML tag that will make they type of page we’re creating more apparent to the browser. Replace the current tag in header.php with the following line.

<html xmlns="http://www.w3.org/1999/xhtml" <?php language_attributes(); ?>>

Now we’re going to get into the get into the section of your WordPress Theme. The 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 style sheets and RSS feeds.

But first open up your functions.php file. We’re going to add a little helper function there, that’ll come in handy when we’re creating the document title. It’s going to give us a page number that we can add to the title.

Start off functions.php with

<?php

and 2 lines down (I like to leave a couple of lines between functions) paste the following 2 PHP functions:

// Make theme available for translation
// Translations can be filed in the /languages/ directory
load_theme_textdomain( 'your-theme', TEMPLATEPATH . '/languages' );

$locale = get_locale();
$locale_file = TEMPLATEPATH . "/languages/$locale.php";
if ( is_readable($locale_file) )
	require_once($locale_file);

// Get the page number
function get_page_number() {
    if ( get_query_var('paged') ) {
        print ' | ' . __( 'Page ' , 'your-theme') . get_query_var('paged');
    }
} // end get_page_number

The first function tells WordPress we want to make our theme available for translation and that translation files can be found in our theme folder in a folder called “languages”. If you’re going to create a WordPress Theme you should always try your best to make sure everything is translatable. You never know when you or someone else is going to need hard-coded content available in another language.

In our next function, get_the_page_number(), you’ll see some translatable text. It looks like this:

__( 'Page ' , 'your-theme')

The text ready for translation is “Page “ followed by the directory name of our theme; in this case, “your-theme”. Pretty straightforward but there are couple other ways to write translatable text. We’ll get to them when we come to them.

Can you guess what get_page_number() is doing? If you take a look inside the function you can see that we’re checking with an IF statement to see if we’re on a paged page, you know, where you are when you click “older posts”. If you are, this function will print a bar separator and the current page number.

Also, If you’re new to PHP I want you to notice something else here. Anything after the double slash (//) is ignored and used as a comment. You’ll be seeing that a lot.

Alright, back to the section of header.php.

Replace with the following code:

<head profile="http://gmpg.org/xfn/11">
    <title><?php
        if ( is_single() ) { single_post_title(); }
        elseif ( is_home() || is_front_page() ) { bloginfo('name'); print ' | '; bloginfo('description'); get_page_number(); }
        elseif ( is_page() ) { single_post_title(''); }
        elseif ( is_search() ) { bloginfo('name'); print ' | Search results for ' . wp_specialchars($s); get_page_number(); }
        elseif ( is_404() ) { bloginfo('name'); print ' | Not Found'; }
        else { bloginfo('name'); wp_title('|'); get_page_number(); }
    ?></title>

	<meta http-equiv="content-type" content="<?php bloginfo('html_type'); ?>; charset=<?php bloginfo('charset'); ?>" />

	<link rel="stylesheet" type="text/css" href="<?php bloginfo('stylesheet_url'); ?>" />

	<?php if ( is_singular() ) wp_enqueue_script( 'comment-reply' ); ?>

	<?php wp_head(); ?>

	<link rel="alternate" type="application/rss+xml" href="<?php bloginfo('rss2_url'); ?>" title="<?php printf( __( '%s latest posts', 'your-theme' ), wp_specialchars( get_bloginfo('name'), 1 ) ); ?>" />
	<link rel="alternate" type="application/rss+xml" href="<?php bloginfo('comments_rss2_url') ?>" title="<?php printf( __( '%s latest comments', 'your-theme' ), wp_specialchars( get_bloginfo('name'), 1 ) ); ?>" />
	<link rel="pingback" href="<?php bloginfo('pingback_url'); ?>" />
</head>

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

A search engine optimized title tag that shows only the post title on single posts and pages. And, of course, adds page numbers with get_page_number() on “older post” pages.

    <title><?php
        if ( is_single() ) { single_post_title(); }
        elseif ( is_home() || is_front_page() ) { bloginfo('name'); print ' | '; bloginfo('description'); get_page_number(); }
        elseif ( is_page() ) { single_post_title(''); }
        elseif ( is_search() ) { bloginfo('name'); print ' | Search results for ' . wp_specialchars($s); get_page_number(); }
        elseif ( is_404() ) { bloginfo('name'); print ' | Not Found'; }
        else { bloginfo('name'); wp_title('|'); get_page_number(); }
    ?></title>

Some meta information about our page content.

	<meta http-equiv="content-type" content="<?php bloginfo('html_type'); ?>; charset=<?php bloginfo('charset'); ?>" />

A link to our stylesheet.

	<link rel="stylesheet" type="text/css" href="<?php bloginfo('stylesheet_url'); ?>" />

A script call so we can use threaded comments when we get to our comments section.

	<?php if ( is_singular() ) wp_enqueue_script( 'comment-reply' ); ?>

A hook for WordPress plugins and other cool stuff.

	<?php wp_head(); ?>

Links for our RSS Feeds and pingbacks.

	<link rel="alternate" type="application/rss+xml" href="<?php bloginfo('rss2_url'); ?>" title="<?php printf( __( '%s latest posts', 'your-theme' ), wp_specialchars( get_bloginfo('name'), 1 ) ); ?>" />
	<link rel="alternate" type="application/rss+xml" href="<?php bloginfo('comments_rss2_url') ?>" title="<?php printf( __( '%s latest comments', 'your-theme' ), wp_specialchars( get_bloginfo('name'), 1 ) ); ?>" />
	<link rel="pingback" href="<?php bloginfo('pingback_url'); ?>" />

The header section

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

In header.php move down to the #branding div. It’s here that we’ll add in the title and description. But we’re going to do something a little different from most themes.

You’ll find that a lot of WordPress Themes tell search engines that the most important thing on every page is your blog title. They do this by wrapping it in an h1 tag; the tag that says “this is what this particular page is all about”. I, and a small group of other theme developers, think this is a bad idea. For instance, the most important thing on this page, the text that defines this particular page, is not “ThemeShaper” but “The WordPress Theme Header Template”. That’s what this page is all about after all. And on our home page, really, the most important thing is the description of our blog. It literally provides an overarching description of all the content on that page.

Luckily, this is easy to take care of.

We’re going to use WordPress’ conditional tags and some plain old HTML to code our title and blog description to do just what best practices describe. Here’s what it looks like:

			<div id="branding">
				<div id="blog-title"><span><a href="<?php bloginfo( 'url' ) ?>/" title="<?php bloginfo( 'name' ) ?>" rel="home"><?php bloginfo( 'name' ) ?></a></span></div>
<?php if ( is_home() || is_front_page() ) { ?>
		    		<h1 id="blog-description"><?php bloginfo( 'description' ) ?></h1>
<?php } else { ?>
		    		<div id="blog-description"><?php bloginfo( 'description' ) ?></div>
<?php } ?>
			</div><!-- #branding -->

So, the text of our anchored blog title is set perfunctorily in a casual div tag. Our blog description is set, using PHP IF statements and some WordPress conditional tags, either as a very important h1-wrapped description on the home page or front page or, like it’s relative the title, in a div everywhere else.

For beginners, this is a good place to step back and see what’s going on. In the code above we’re using a WordPress Template Tag called bloginfo(). You can see we’re using it to get the URL of our WordPress blog, the name of our blog, and the description. 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 #access div. 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

				<div class="skip-link"><a href="#content" title="<?php _e( 'Skip to content', 'your-theme' ) ?>"><?php _e( 'Skip to content', 'your-theme' ) ?></a></div>

and we’re going to add our page menu which, really couldn’t be any easier as it’s just one template tag, with only 1 argument:

				<?php wp_page_menu( 'sort_column=menu_order' ); ?>

Easy, right? So your #access div should look like this:

			<div id="access">
				<div class="skip-link"><a href="#content" title="<?php _e( 'Skip to content', 'your-theme' ) ?>"><?php _e( 'Skip to content', 'your-theme' ) ?></a></div>
				<?php wp_page_menu( 'sort_column=menu_order' ); ?>
			</div><!-- #access -->

And that’s it! Your WordPress Theme Header Template is search engine optimized and coded up.

How To Create a WordPress Theme

This post is part of a WordPress Themes Tutorial 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. Theme Development Tools
  3. Creating a Theme HTML Structure
  4. Template and Directory Structure
  5. The Header Template
  6. The Index Template
  7. The Single Post, Post Attachment, & 404 Templates
  8. The Comments Template
  9. The Search Template & The Page Template
  10. The Archive, Author, Category & Tags Template
  11. The Sidebar Template
  12. Reset-Rebuild Theme CSS & Define Your Layouts

65 responses

  1. Is the transitional XHTML because of the plugin compatibility?

    great series, I’m following along and learning a lot, thanks

    1. Yep. And to keep it valid if a user writes something less than valid in the HTML editor.

  2. shouldn’t the first line of the first function be :
    load_theme_textdomain( ‘your-theme’, TEMPLATEPATH . ‘/languages’ ) {
    instead of
    load_theme_textdomain( ‘your-theme’, TEMPLATEPATH . ‘/languages’ );

    1. ^^ no wtp because it’s not a conditional

      1. The function ‘load_theme_textdomain’ is defined in a WordPress core file. So the question is really, “What is this function call doing in our theme’s function.php file?” Shouldn’t function.php only contain our theme’s custom defined functions?

        See File: ../wp-includes/i10n.php

        function load_theme_textdomain($domain, $path = false) {
        $locale = get_locale();
        $path = ( empty( $path ) ) ? get_template_directory() : $path;
        $mofile = “$path/$locale.mo”;
        return load_textdomain($domain, $mofile);
        }

  3. Ian — Think about putting in a screenie of your main page so we can compare it to ours. That way we can be sure all code is correct?

    JIM

    1. Perhaps. I may make “Your-Theme” an open source project and just link to the raw files in each “chapter”. I’ll do something. 🙂

  4. Thanks for this tutorial – I must admit I’ve been waiting eagerly for the next step to come up each day. While I agree about not using the blog title as h1 for any page other than the home – I don’t think that the blog description field really gets used in practice as a description, it seems to be used much more as a tagline, which makes it less appropriate to be used as a heading.

    But that’s fairly immaterial: thanks for doing this, I’m learning plenty and really enjoying your insights.

  5. Excellent tutorial, thanks 🙂

    I’ve build WordPress themes before, but this is cleaning up some of the finer points!

    I’ll be following the next parts avidly.

  6. Thanks Ian — this is incredibly useful (and beautifully presented).

  7. I roared when I saw Geoff comment on eagerly awaiting the next step. I went to bed about 1:30 this morning, woke up about 4:00 for bathroom. Most people go right back to bed, not me! I went to see if Ian had put up next step!!!! Now tell me Ian is not doing a superb job.

  8. I’m glued to your site for this series. A series that is worth its weight in diamonds 🙂

    I also agree with your comment above, an open source version of the theme would be awesome to compare notes, ect.

    1. For now, you can check out the Shape Theme. The code is essentially the same.

    2. Mike, you can now browse and checkout the code on Google Code. Hope that helps!

  9. Todrick Moore Avatar
    Todrick Moore

    First let me say thank you for the series. I am enjoying it very much. I have two questions:
    1. Where and when will we see the function get_page_number in action?
    2. How can this project be turned into a framework like Thematic or any of the others?

    1. 1. In this very tutorial you’ve been reading, in the title tag.
      2. With lots of patience and care! 🙂 I may talk about this in a future post.

      1. Hopefully in the near future you’ll also consider doing a series on how to build a basic theme framework. I’ve been reading Justin’s bullet points on what features a theme framework should have and it can be good starting points. Cheers!

  10. Todrick and Ian,

    If anyone has doubts about the power of Thematic please do as I did and look at
    Cristi has made a child theme from Thematic and if you look at the code you will see what a great system Ian has developed. In agreement with Rodrick about tailoring this project towards a thematic framework.

  11. […] here: The WordPress Theme Header Template Share and […]

  12. Keep up the amazing work Ian. Quick question: The “ unnecessary?

    1. eeek! Cursed backticks! (Squawk at your friend Mr. Tadlock who got me used to sticking code between them) — What I meant to say, after telling you how fabulous this continues to be, was that the <?php opener in functions.php appears to have no closing tag. Is this something you intended, or is a closing tag unnecessary? Just trying to avoid fatal errors and stuff.

      1. Yep, a closing PHP tag is unnecessary.

  13. Thanks for the tutorials. So far enjoying them greatly.

    I’m not sure if this has happened to anyone else. But this always happens to me. Having a blank functions.php file in the themes directory seriously kills wordpress. It’s not a big problem cause I can just delete the file or switch themes if that screen still lives. Or simply add a function into the file.

    Anyway, just thought I’d let people know in case they come across that.

    🙂

  14. […] 原文:The WordPress Theme Header Template […]

  15. I am just wondering why the text in the tag is not passed to _e() function

    Was it done on purpose?

    1. Do you mean the title tag?

      1. Yes I meant the title tag.

        (I guess the HTML tags were stripped from my comment earlier)

    2. Ah. That would be a mistake, then. 🙂 I’ll get that fixed up.

      1. Daniel Balfour Avatar
        Daniel Balfour

        Ian,

        I’m wondering if I may have found a bug?

        In this tutorial you stated:

        So, the text of our anchored blog title is set perfunctorily in a casual div tag. Our blog description is set, using PHP IF statements and some WordPress conditional tags, either as a very important h1-wrapped description on the home page or front page or, like it’s relative the title, in a div everywhere else.

        I just checked Thematic and my ‘Blog Description’ is wrapped in a plain old div tag (the blog title is as well, but that’s to be expected). I am of course referring to the home page and not any specific post/page pages.

        I could be making a mistake, but I’m pretty sure I went through the source code rather thoroughly.
        I’m developing a child theme for Thematic.

        Thoughts?

      2. Daniel Balfour Avatar
        Daniel Balfour

        Ian – my bad. I forgot that the child theme also takes php files from its own directory before going over to the parent theme’s directory.

        My bad – lessons learned.

  16. Hey! great tutorial so far!! I am wondering one thing, for all the files created is there a designated file for .html? I see the header file contains all of or a lot of the html, is that where it will be called from under the .php extension?

    thanks!
    Colin

  17. Dear Ian

    First of all, I would like to give you a very Big THANKS for this series.
    It’s by far the most updated and best documented version of how to create wp theme.

    For the header template, I have one question if you don’t mind.

    It’s about this part –>

    I was wondering if we still need this ?

    From my understanding, that line is like a patch for pre-2.7 theme
    (so that it’s compatible with threaded comment thing in 2.7 and higher)

    The other theme framework ( other than thematic) that I’m currently studying doesn’t have that line in the header template, but it uses ….if ( ( get_option(‘thread_comments’) )… in its custom comment callback function.

    I guess that will automatically call the comment-reply.js without that line in the header template.

    Please correct me if I’m wrong.

    and here again, Big Thanks for this series, really really GREAT posts.

  18. Oops ! here we go again.

    It’s about this part –> if ( is_singular() ) wp_enqueue_script( ‘comment-reply’ )

  19. With the skip-link div, can the skip-link class be added to the anchor tag and drop the extra div?

  20. Hi, great tutorial.

    Im planning to use your Shape-theme as my theme framework. Do you have the po-file so I can translate it to swedish?

  21. Hi Ian!

    Thanks a lot for your tutorial, I’m learning a lot! And having fun also, with your sense of humor.

    In the latest update to Thematic, languages is in library/languages directory. Then, shouldn’t

    load_theme_textdomain( ‘your-theme’, TEMPLATEPATH . ‘/languages’ );

    and

    $locale_file = TEMPLATEPATH . “/languages/$locale.php”;

    mention …”/library/languages/… ??

    Thanks.
    Gabriela

  22. Ah! Should a put a copy of library folder in my child Thematic theme?

  23. justkeepblogging Avatar
    justkeepblogging

    Ian,

    I’ve tried this a few times and every time I get to this part I get the following error:

    Fatal error: Call to undefined function get_page_number() in C:xampphtdocswordpresswp-contentthemesthematicheader.php on line 7

    I’ve checked the code against the google code and it looks the same. What could I be doing wrong?

    Thanks for any help you can offer.

    1. jspin77 Avatar

      I too am getting the error… did you ever find a solution as to why?

      1. Did you check to make sure the initial <?php is all “one word” ?

  24. Hi Ian, first of all thanks for Thematic theme.
    I love its essential look as ideal platform for loads of customizations.
    I’m not smart with codes so I use some wp plugins to build a little e-shop.
    I use wp-ecommerce by Instinct and I’d like to insert the shopping basket in the empty right side of header. (I deleted sidebar to have a wide square where to put all products images and descriptions)
    Instinct provides this shortcode to add the cart to the sidebar or header
    Please can you help me how to add this code to the header?
    Many thanks

  25. This is one of the most comprehensive and thorough tutorials on WordPress template development I’ve come across. Some very useful tips on the header section, however I did miss one thing though. Why no meta description? Whilst it’s true that it offers no benefit with regards to actual search engine ranking it is vital for maintaining some control of what is actually displayed in the SERPs.

  26. Hi

    Nice tutorial, but for some reason, fixed width?, some of the text disappear under the right column, making it very hard to understand.

    I’m on a 1280×800 laptop, nothing special, so I suppose others must have seen this as well.

    regards

    Vito

    1. By the way, tested this in both ie8, ff3 and chrome.

      Disabled css and kept reading this very fine tutorial….

      1. I noticed that too when I came back to read the article again. It looks like the plugin that formats code is disabled or non-functional. It worked fine about a week ago.

  27. Hey Ian, really great tutorial so far (I’m currently on step 5).

    Quick question so I’m not spinning my wheels, should this tutorial work fine with the latest WP 2.9 release? If not, do you know what steps need to be modified, or would you need to completely retool the tutorial?

    Thanks a mil, buddy 🙂

  28. I have only recently taken up WordPress, and have been looking for something like this for the past week! So far I’m following along and understanding well, this is great.

  29. Now that I’ve followed through your tutorials, how do I change my blog-title to an image? Been trying to figure this out for a little while but have had no sucess

  30. […] Sumber: The WordPress Theme Header Template. […]

  31. Great tutorial, mate! One thing though.. Your PHP knowledge and use of ID’s is terrible!

  32. Great framework…. im using wordpress for 1 years. Finnaly i can build my own theme. Thank for your good job… ^^

  33. Thanks so much for this tutorial, Ian! I’ve especially been wracking my brain over how to include page numbers in the page titles, and in one fell swoop, you’ve solved that, as well as providing different page titles for search, categories… The works.

    Amazing.

    One comment / suggestion: in my case, I have a static page for the frontpage, and that means that there’s no distinction between the site’s frontpage proper and the ‘blog page’ that contains the X latest posts. That means I’m more prone to “duplicate title” reports in Google Analytics, and the Google Webmaster Tool reports. So, I’ve taken the liberty of adjusting the code a bit, splitting up the logical “or” you have set, and add a small static text to the ‘blog’ page which I use for news type items, like this:


    Thanks again, Ian – much obliged!

  34. Well, I just finished the tutorial, created all the files by copying the code from the web site. Alas, I had to remove the line numbers, and I had to replace the , as they didn’t paste in as two dashes on each end when I copied the code. After that, I plugged a couple of pages into the W3c validation service at http://validator.w3.org/ and had a mess on my hands. It wouldn’t read the files, and I got this message.
    —————————-
    Sorry! This document can not be checked.
    Sorry, I am unable to validate this document because its content type is application/octet-stream, which is not currently supported by this service.
    —————————-
    So, I copied the code directly into the direct output window and got a rash of errors and warnings.

    As careful as I was in the process, I figured I made mistakes when editing the line numbers and the stuff. Then, when I saw the W3C validation on the first two or three files I checked, I couldn’t believe that the rash of errors were all my doing.

    Alas, I went to this site:
    http://code.google.com/p/your-wordpress-theme/source/browse/#svn/trunk
    and copied the code for the header.php file since it had the DOCTYPE in it. I had the same problem with the W3C validation: It didn’t recognize the file type and wouldn’t attempt to validate it. So, I pasted the code into the direct output window and got 10 errors and 14 warnings.

    I get a site when I look at it, but W3c says the code is a mess.

    I used the validation page at: http://validator.w3.org/
    Should I use a different validator?
    What’s goin’ on.
    Am I doin’ something wrong?
    I have an associates degree in programming from a few years ago, so I understand fundamental code structure, and even though I’ve built a couple of sites on my own, this is the first time I have tried to construct a WordPress theme from, so-to-speak, scratch.
    Am I about to learn something, here?
    Any help is appreciated.

    1. I have a similar problem as Joe. I followed the same logic as he. The HTML validator gives me this message:
      ” Sorry, I am unable to validate this document because on line 181 it contained one or more bytes that I cannot interpret as utf-8 (in other words, the bytes found are not valid values in the specified Character Encoding). Please check both the content of the file and the character encoding indication.

      The error was: utf8 “xD0″ does not map to Unicode ”

      What should we do?

      Thank you!
      David

  35. […] Best Damn WordPress Theme Guide EVER: https://themeshaper.com/wordpress-theme-header-template-tutorial/ WordPress Codex – Theme Dev: http://codex.wordpress.org/Theme_Development Things I really […]

  36. Very simple so far. So glad I found this page. I just hope I’ll be able to implement a design idea I have though about several weeks ago.

  37. Hi Ian,
    Thank you so much for this tutorial, it’s been incredibly helpful to me.

    I have a question regarding the “your-theme” parameter found in much of this code. What purpose is it serving? Is it there to stylize text output according to where the parameter points?

    Thanks Again!
    Mike

    1. That’s there to aid in the translation of the theme. I’ll pick up on that in updates to the tutorial.

  38. Hi Ian. Thanks for this great tutorial.
    shouldn’t there be an ending php tag in functions.php? Am I missing something?

    1. The ending PHP tag isn’t necessary when the document is one long script.