The WordPress Theme Search Template and Page Template

The Search Template and The Page Template are vital to any complete WordPress Theme. And they’re both really easy to code. For both these two Templates we’ll start with our template-template again but, of course, each one is going to take it’s own different path.

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 Search Template and Page Template. It’s part of The ThemeShaper WordPress Theme Tutorial: 2nd Edition.

The Search Template and The Page Template are vital to any complete WordPress Theme. And they’re both really easy to code. For both these two Templates we’ll start with our template-template again.

<?php get_header(); ?>
	
		<div id="container">	
			<div id="content">
			
				<div id="nav-above" class="navigation">
				</div><!-- #nav-above -->

				<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
				</div><!-- #post-<?php the_ID(); ?> -->			
				
				<div id="nav-below" class="navigation">
				</div><!-- #nav-below -->					

			
			</div><!-- #content -->		
		</div><!-- #container -->
		
<?php get_sidebar(); ?>	
<?php get_footer(); ?>

But, of course, each one is going to take it’s own different path.

The Search Template

In search.php we’re going to reintroduce the loop back into our Template. This time with an IF statement—in case we don’t have any posts to loop through.

Here’s how it’ll work: IF we have posts, or, in other words, if there are posts that match the terms we’re searching for, THEN loop through them, sorta just like in index.php, but IF we don’t have posts to loop through, or, if there aren’t any posts that match our search terms, give our searchers another chance at this searching business.

In code, it would look like this:

<?php get_header(); ?>
	
		<div id="container">	
			<div id="content">
			
<?php if ( have_posts() ) : ?>				

<?php while ( have_posts() ) : the_post() ?>
<!-- this is our loop -->
<?php endwhile; ?>

<?php else : ?>

<!-- here's where we'll put a search form if there're no posts -->

<?php endif; ?>			

			</div><!-- #content -->		
		</div><!-- #container -->
		
<?php get_sidebar(); ?>	
<?php get_footer(); ?>

Pretty straightforward, right? Almost.

I like to keep all my index-ey Templates the same: Post Title, Meta, Content (or excerpt), Utility links. But when WordPress searches for posts it also searches through Pages, which don’t need the post meta or utility links displayed. So, in our loop, we’re going to check and see if we’re dealing with a post or a page.

<?php if ( $post->post_type == 'post' ) { ?>
<?php } ?>

Wrap any code with that IF statement and it will only show if we’re dealing with a page. Now that we understand what’s going on, here’s what the #content div of our search template will look like:

<?php if ( have_posts() ) : ?>
				
				<h1 class="page-title"><?php _e( 'Search Results for: ', 'your-theme' ); ?><span><?php the_search_query(); ?></span></h1>
				
<?php global $wp_query; $total_pages = $wp_query->max_num_pages; if ( $total_pages > 1 ) { ?>
				<div id="nav-above" class="navigation">
					<div class="nav-previous"><?php next_posts_link(__( '<span class="meta-nav">&laquo;</span> Older posts', 'your-theme' )) ?></div>
					<div class="nav-next"><?php previous_posts_link(__( 'Newer posts <span class="meta-nav">&raquo;</span>', 'your-theme' )) ?></div>
				</div><!-- #nav-above -->
<?php } ?>							

<?php while ( have_posts() ) : the_post() ?>

				<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
					<h2 class="entry-title"><a href="<?php the_permalink(); ?>" title="<?php printf( __('Permalink to %s', 'your-theme'), the_title_attribute('echo=0') ); ?>" rel="bookmark"><?php the_title(); ?></a></h2>

<?php if ( $post->post_type == 'post' ) { ?>									
					<div class="entry-meta">
						<span class="meta-prep meta-prep-author"><?php _e('By ', 'your-theme'); ?></span>
						<span class="author vcard"><a class="url fn n" href="<?php echo get_author_link( false, $authordata->ID, $authordata->user_nicename ); ?>" title="<?php printf( __( 'View all posts by %s', 'your-theme' ), $authordata->display_name ); ?>"><?php the_author(); ?></a></span>
						<span class="meta-sep"> | </span>
						<span class="meta-prep meta-prep-entry-date"><?php _e('Published ', 'your-theme'); ?></span>
						<span class="entry-date"><abbr class="published" title="<?php the_time('Y-m-dTH:i:sO') ?>"><?php the_time( get_option( 'date_format' ) ); ?></abbr></span>
						<?php edit_post_link( __( 'Edit', 'your-theme' ), "<span class="meta-sep">|</span>ntttttt<span class="edit-link">", "</span>nttttt" ) ?>
					</div><!-- .entry-meta -->
<?php } ?>
					
					<div class="entry-summary">	
<?php the_excerpt( __( 'Continue reading <span class="meta-nav">&raquo;</span>', 'your-theme' )  ); ?>
<?php wp_link_pages('before=<div class="page-link">' . __( 'Pages:', 'your-theme' ) . '&after=</div>') ?>
					</div><!-- .entry-summary -->

<?php if ( $post->post_type == 'post' ) { ?>									
					<div class="entry-utility">
						<span class="cat-links"><span class="entry-utility-prep entry-utility-prep-cat-links"><?php _e( 'Posted in ', 'your-theme' ); ?></span><?php echo get_the_category_list(', '); ?></span>
						<span class="meta-sep"> | </span>
						<?php the_tags( '<span class="tag-links"><span class="entry-utility-prep entry-utility-prep-tag-links">' . __('Tagged ', 'your-theme' ) . '</span>', ", ", "</span>ntttttt<span class="meta-sep">|</span>n" ) ?>
						<span class="comments-link"><?php comments_popup_link( __( 'Leave a comment', 'your-theme' ), __( '1 Comment', 'your-theme' ), __( '% Comments', 'your-theme' ) ) ?></span>
						<?php edit_post_link( __( 'Edit', 'your-theme' ), "<span class="meta-sep">|</span>ntttttt<span class="edit-link">", "</span>ntttttn" ) ?>
					</div><!-- #entry-utility -->	
<?php } ?>					
				</div><!-- #post-<?php the_ID(); ?> -->

<?php endwhile; ?>

<?php global $wp_query; $total_pages = $wp_query->max_num_pages; if ( $total_pages > 1 ) { ?>
				<div id="nav-below" class="navigation">
					<div class="nav-previous"><?php next_posts_link(__( '<span class="meta-nav">&laquo;</span> Older posts', 'your-theme' )) ?></div>
					<div class="nav-next"><?php previous_posts_link(__( 'Newer posts <span class="meta-nav">&raquo;</span>', 'your-theme' )) ?></div>
				</div><!-- #nav-below -->
<?php } ?>			

<?php else : ?>

				<div id="post-0" class="post no-results not-found">
					<h2 class="entry-title"><?php _e( 'Nothing Found', 'your-theme' ) ?></h2>
					<div class="entry-content">
						<p><?php _e( 'Sorry, but nothing matched your search criteria. Please try again with some different keywords.', 'your-theme' ); ?></p>
	<?php get_search_form(); ?>						
					</div><!-- .entry-content -->
				</div>

<?php endif; ?>	

The Page Template

You know what the Page Template is for. WordPress thinks of it as a post out of chronological order. We think of it as, well a page. But largely, it’s defined by what it doesn’t have: all the usual trappings of a blog post.

Except comments. Sometimes pages have comments. I don’t like them there. You might one day. So we’ll have to deal with that somehow. How about with … custom fields. If you want comments on a page you can add a custom field with Name and Value of “comments” to your page. Sounds good to me.

Here’s what you’ll need to for a perfect WordPress Page Template:

<?php get_header(); ?>
	
		<div id="container">	
			<div id="content">
			
<?php the_post(); ?>
				
				<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
					<h1 class="entry-title"><?php the_title(); ?></h1>
					<div class="entry-content">
<?php the_content(); ?>
<?php wp_link_pages('before=<div class="page-link">' . __( 'Pages:', 'your-theme' ) . '&after=</div>') ?>					
<?php edit_post_link( __( 'Edit', 'your-theme' ), '<span class="edit-link">', '</span>' ) ?>
					</div><!-- .entry-content -->
				</div><!-- #post-<?php the_ID(); ?> -->			
			
<?php if ( get_post_custom_values('comments') ) comments_template() // Add a custom field with Name and Value of "comments" to enable comments on this page ?>			
			
			</div><!-- #content -->		
		</div><!-- #container -->
		
<?php get_sidebar(); ?>	
<?php get_footer(); ?>

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

The WordPress Theme Comments 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 Comments Template. It’s part of The ThemeShaper WordPress Theme Tutorial: 2nd Edition.

I hate the Comments Template. There, I said it. It can be a confusing mess. In version 2.7, WordPress introduced a simpler way of producing Comments Templates—which was no help if you wanted to separate your comments and trackbacks or have custom comment markup. It’s still confusing.

Luckily for you, I’ve sorted it out. Confusing still, yes. But sorted out. For this tutorial on the Comments Template I’m basically going to walk you through what’s going to happen, show you some custom code snippets you’ll need to add to your functions.php, and then drop the whole thing on you. Hopefully, it’ll start to make sense. But at they very least you’ll have a wicked comments template.

Let’s take a look at a quick list of what’s going on in this Template.

  1. Prevent loading for bots and on password protected posts
  2. Check if there are comments
  3. Count the number of comments and trackbacks (or pings)
  4. If there are comments, show the comments—with navigation for paginated comments
  5. If there are trackbacks, show the trackbacks
  6. If comments are open, show the comments “respond” form

That’s a lot of stuff going on for one template. But written out like that, it’s pretty straightforward.

Custom Callbacks for Comments and Trackbacks

Now, with WordPress 2.7 came the function wp_list_comments() that conveniently spits out an ordered list of comments and trackbacks markup for your post (threaded too). Convenient if you want that. And we don’t. We want separated threaded comments and trackbacks, with our own custom markup.

To make the comments template code I’m going to give you work, you’ll need a set of custom callbacks for your list of Comments and Trackbacks. Add the following 2 functions to your theme functions.php file.

// Custom callback to list comments in the your-theme style
function custom_comments($comment, $args, $depth) {
  $GLOBALS['comment'] = $comment;
	$GLOBALS['comment_depth'] = $depth;
  ?>
  	<li id="comment-<?php comment_ID() ?>" <?php comment_class() ?>>
  		<div class="comment-author vcard"><?php commenter_link() ?></div>
  		<div class="comment-meta"><?php printf(__('Posted %1$s at %2$s <span class="meta-sep">|</span> <a href="%3$s" title="Permalink to this comment">Permalink</a>', 'your-theme'),
  					get_comment_date(),
  					get_comment_time(),
  					'#comment-' . get_comment_ID() );
  					edit_comment_link(__('Edit', 'your-theme'), ' <span class="meta-sep">|</span> <span class="edit-link">', '</span>'); ?></div>
  <?php if ($comment->comment_approved == '0') _e("ttttt<span class='unapproved'>Your comment is awaiting moderation.</span>n", 'your-theme') ?>
          <div class="comment-content">
      		<?php comment_text() ?>
  		</div>
		<?php // echo the comment reply link
			if($args['type'] == 'all' || get_comment_type() == 'comment') :
				comment_reply_link(array_merge($args, array(
					'reply_text' => __('Reply','your-theme'),
					'login_text' => __('Log in to reply.','your-theme'),
					'depth' => $depth,
					'before' => '<div class="comment-reply-link">',
					'after' => '</div>'
				)));
			endif;
		?>
<?php } // end custom_comments
// Custom callback to list pings
function custom_pings($comment, $args, $depth) {
       $GLOBALS['comment'] = $comment;
        ?>
    		<li id="comment-<?php comment_ID() ?>" <?php comment_class() ?>>
    			<div class="comment-author"><?php printf(__('By %1$s on %2$s at %3$s', 'your-theme'),
    					get_comment_author_link(),
    					get_comment_date(),
    					get_comment_time() );
    					edit_comment_link(__('Edit', 'your-theme'), ' <span class="meta-sep">|</span> <span class="edit-link">', '</span>'); ?></div>
    <?php if ($comment->comment_approved == '0') _e('ttttt<span class="unapproved">Your trackback is awaiting moderation.</span>n', 'your-theme') ?>
            <div class="comment-content">
    			<?php comment_text() ?>
			</div>
<?php } // end custom_pings

Those look kind of hairy don’t they? But you’re better off for it. Now you have access to the comments markup. I think the markup I’ve got in there is pretty sweet and will let you make a lot of changes with just CSS alone, but if you do want to alter the markup, well, there it is.

We’ll also need a special custom function that the custom_comments() is calling. This function will markup the gravatar we’re using so it fits into the microformat schema for hcard.

// Produces an avatar image with the hCard-compliant photo class
function commenter_link() {
	$commenter = get_comment_author_link();
	if ( ereg( '<a[^>]* class=[^>]+>', $commenter ) ) {
		$commenter = ereg_replace( '(<a[^>]* class=['"]?)', '\1url ' , $commenter );
	} else {
		$commenter = ereg_replace( '(<a )/', '\1class="url "' , $commenter );
	}
	$avatar_email = get_comment_author_email();
	$avatar = str_replace( "class='avatar", "class='photo avatar", get_avatar( $avatar_email, 80 ) );
	echo $avatar . ' <span class="fn n">' . $commenter . '</span>';
} // end commenter_link

If you want to change the default size of your gravatar just change the 80 in get_avatar( $avatar_email, 80 ) ). The 80 is the size in pixels of your gravatar.

The Comments Template

I haven’t scared you away have I? I’ll be honest, it’s not that scary. Here’s the comments template with some helpful PHP comments that should guide you along in understanding what’s happening.

<?php /* The Comments Template — with, er, comments! */ ?>
			<div id="comments">
<?php /* Run some checks for bots and password protected posts */ ?>
<?php
	$req = get_option('require_name_email'); // Checks if fields are required.
	if ( 'comments.php' == basename($_SERVER['SCRIPT_FILENAME']) )
		die ( 'Please do not load this page directly. Thanks!' );
	if ( ! empty($post->post_password) ) :
		if ( $_COOKIE['wp-postpass_' . COOKIEHASH] != $post->post_password ) :
?>
				<div class="nopassword"><?php _e('This post is password protected. Enter the password to view any comments.', 'your-theme') ?></div>
			</div><!-- .comments -->
<?php
		return;
	endif;
endif;
?>

<?php /* See IF there are comments and do the comments stuff! */ ?>
<?php if ( have_comments() ) : ?>

<?php /* Count the number of comments and trackbacks (or pings) */
$ping_count = $comment_count = 0;
foreach ( $comments as $comment )
	get_comment_type() == "comment" ? ++$comment_count : ++$ping_count;
?>

<?php /* IF there are comments, show the comments */ ?>
<?php if ( ! empty($comments_by_type['comment']) ) : ?>

				<div id="comments-list" class="comments">
					<h3><?php printf($comment_count > 1 ? __('<span>%d</span> Comments', 'your-theme') : __('<span>One</span> Comment', 'your-theme'), $comment_count) ?></h3>

<?php /* If there are enough comments, build the comment navigation  */ ?>
<?php $total_pages = get_comment_pages_count(); if ( $total_pages > 1 ) : ?>
					<div id="comments-nav-above" class="comments-navigation">
								<div class="paginated-comments-links"><?php paginate_comments_links(); ?></div>
					</div><!-- #comments-nav-above -->
<?php endif; ?>

<?php /* An ordered list of our custom comments callback, custom_comments(), in functions.php   */ ?>
					<ol>
<?php wp_list_comments('type=comment&amp;callback=custom_comments'); ?>
					</ol>

<?php /* If there are enough comments, build the comment navigation */ ?>
<?php $total_pages = get_comment_pages_count(); if ( $total_pages > 1 ) : ?>
	  			<div id="comments-nav-below" class="comments-navigation">
						<div class="paginated-comments-links"><?php paginate_comments_links(); ?></div>
	        </div><!-- #comments-nav-below -->
<?php endif; ?>

				</div><!-- #comments-list .comments -->

<?php endif; /* if ( $comment_count ) */ ?>

<?php /* If there are trackbacks(pings), show the trackbacks  */ ?>
<?php if ( ! empty($comments_by_type['pings']) ) : ?>

				<div id="trackbacks-list" class="comments">
					<h3><?php printf($ping_count > 1 ? __('<span>%d</span> Trackbacks', 'your-theme') : __('<span>One</span> Trackback', 'your-theme'), $ping_count) ?></h3>

<?php /* An ordered list of our custom trackbacks callback, custom_pings(), in functions.php   */ ?>
					<ol>
<?php wp_list_comments('type=pings&amp;callback=custom_pings'); ?>
					</ol>

				</div><!-- #trackbacks-list .comments -->

<?php endif /* if ( $ping_count ) */ ?>
<?php endif /* if ( $comments ) */ ?>

<?php /* If comments are open, build the respond form */ ?>
<?php if ( 'open' == $post->comment_status ) : ?>
				<div id="respond">
    				<h3><?php comment_form_title( __('Post a Comment', 'your-theme'), __('Post a Reply to %s', 'your-theme') ); ?></h3>

    				<div id="cancel-comment-reply"><?php cancel_comment_reply_link() ?></div>

<?php if ( get_option('comment_registration') &amp;&amp; !$user_ID ) : ?>
					<p id="login-req"><?php printf(__('You must be <a href="%s" title="Log in">logged in</a> to post a comment.', 'your-theme'),
					get_option('siteurl') . '/wp-login.php?redirect_to=' . get_permalink() ) ?></p>

<?php else : ?>
					<div class="formcontainer">

						<form id="commentform" action="<?php echo get_option('siteurl'); ?>/wp-comments-post.php" method="post">

<?php if ( $user_ID ) : ?>
							<p id="login"><?php printf(__('<span class="loggedin">Logged in as <a href="%1$s" title="Logged in as %2$s">%2$s</a>.</span> <span class="logout"><a href="%3$s" title="Log out of this account">Log out?</a></span>', 'your-theme'),
								get_option('siteurl') . '/wp-admin/profile.php',
								wp_specialchars($user_identity, true),
								wp_logout_url(get_permalink()) ) ?></p>

<?php else : ?>

							<p id="comment-notes"><?php _e('Your email is <em>never</em> published nor shared.', 'your-theme') ?> <?php if ($req) _e('Required fields are marked <span class="required">*</span>', 'your-theme') ?></p>

              <div id="form-section-author" class="form-section">
								<div class="form-label"><label for="author"><?php _e('Name', 'your-theme') ?></label> <?php if ($req) _e('<span class="required">*</span>', 'your-theme') ?></div>
								<div class="form-input"><input id="author" name="author" type="text" value="<?php echo $comment_author ?>" size="30" maxlength="20" tabindex="3" /></div>
              </div><!-- #form-section-author .form-section -->

              <div id="form-section-email" class="form-section">
								<div class="form-label"><label for="email"><?php _e('Email', 'your-theme') ?></label> <?php if ($req) _e('<span class="required">*</span>', 'your-theme') ?></div>
								<div class="form-input"><input id="email" name="email" type="text" value="<?php echo $comment_author_email ?>" size="30" maxlength="50" tabindex="4" /></div>
              </div><!-- #form-section-email .form-section -->

              <div id="form-section-url" class="form-section">
								<div class="form-label"><label for="url"><?php _e('Website', 'your-theme') ?></label></div>
								<div class="form-input"><input id="url" name="url" type="text" value="<?php echo $comment_author_url ?>" size="30" maxlength="50" tabindex="5" /></div>
              </div><!-- #form-section-url .form-section -->

<?php endif /* if ( $user_ID ) */ ?>

              <div id="form-section-comment" class="form-section">
								<div class="form-label"><label for="comment"><?php _e('Comment', 'your-theme') ?></label></div>
								<div class="form-textarea"><textarea id="comment" name="comment" cols="45" rows="8" tabindex="6"></textarea></div>
              </div><!-- #form-section-comment .form-section -->

              <div id="form-allowed-tags" class="form-section">
	              <p><span><?php _e('You may use these <abbr title="HyperText Markup Language">HTML</abbr> tags and attributes:', 'your-theme') ?></span> <code><?php echo allowed_tags(); ?></code></p>
              </div>

<?php do_action('comment_form', $post->ID); ?>

							<div class="form-submit"><input id="submit" name="submit" type="submit" value="<?php _e('Post Comment', 'your-theme') ?>" tabindex="7" /><input type="hidden" name="comment_post_ID" value="<?php echo $id; ?>" /></div>

<?php comment_id_fields(); ?>

<?php /* Just … end everything. We're done here. Close it up. */ ?>

						</form><!-- #commentform -->
					</div><!-- .formcontainer -->
<?php endif /* if ( get_option('comment_registration') &amp;&amp; !$user_ID ) */ ?>
				</div><!-- #respond -->
<?php endif /* if ( 'open' == $post->comment_status ) */ ?>
			</div><!-- #comments -->

And that’s it. You’ve got a pretty sweet custom Comments Template to call your very own.

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

The WordPress Theme Single Post, Post Attachment, & 404 Templates

You’ve built an index of all your posts, now you need to create a template to frame each piece of content (or missing content) on it’s own. The structure of single.php (and almost all the other templates we’ll be creating) is largely the same as index.php. In fact you can think of it as our template-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 Single Post, Post Attachment, & 404 Templates. It’s part of The ThemeShaper WordPress Theme Tutorial: 2nd Edition.

You’ve built an index of all your posts, now you need to create a template to frame each piece of content (or missing content) on it’s own.

The Template for Templates

The structure of single.php (and almost all the other templates we’ll be creating) is largely the same as index.php. In fact you can think of it as our template-template.

<?php get_header(); ?>

		<div id="container">
			<div id="content">

				<div id="nav-above" class="navigation">
				</div><!-- #nav-above -->

				<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
				</div><!-- #post-<?php the_ID(); ?> -->

				<div id="nav-below" class="navigation">
				</div><!-- #nav-below -->

			</div><!-- #content -->
		</div><!-- #container -->

<?php get_sidebar(); ?>
<?php get_footer(); ?>

But there are going to be some notable differences. Starting with the_post() and comments_template().

We’ll be calling the_post() near the top of our page just after the opening of the content div and just before the navigation. We won’t need a loop in this template as WordPress knows just what post we’re looking at thanks to the_permalink().

And since this is a single post, we’ll need the comments_template(). And because we’ll be separating our comments and trackbacks when we come to coding up comments.php, we need to call it just like so:

<?php comments_template('', true); ?>

comments_template() will need to go just before the close of the #content div right after the navigation.

Single Post Navigation

Instead of using the poorly named next_posts_link() and previous_posts_link() we’ll be using the mostly accurately named previous_post_link() and next_post_link(). They do just what you think they do.

				<div id="nav-above" class="navigation">
					<div class="nav-previous"><?php previous_post_link( '%link', '<span class="meta-nav">&amp;laquo;</span> %title' ) ?></div>
					<div class="nav-next"><?php next_post_link( '%link', '%title <span class="meta-nav">&amp;raquo;</span>' ) ?></div>
				</div><!-- #nav-above -->
				<div id="nav-below" class="navigation">
					<div class="nav-previous"><?php previous_post_link( '%link', '<span class="meta-nav">&amp;laquo;</span> %title' ) ?></div>
					<div class="nav-next"><?php next_post_link( '%link', '%title <span class="meta-nav">&amp;raquo;</span>' ) ?></div>
				</div><!-- #nav-below -->

Single Post Titles

If you remember from our header.php lesson, we used a dynamic IF statement to clear the way for our Single post titles to take precedence on the page in the eyes of screen readers. We take advantage of this in this and all the rest of our Theme Templates by wrapping the title in and h1 tag.

					<h1 class="entry-title"><?php the_title(); ?></h1>

You’ll notice that our post title code is a little simpler too. The benefit of not having to link to anything now.

Single Post Entry Utility Links

The entry utility is … complicated. Here I think you’ll see the benefit of getting something right once and standing on the shoulders of others.

Before we take a look at the code we should think about why it is complicated. Because of the way comments work in WordPress we need to account for a few different scenarios: Open comments and trackbacks; only trackbacks open; only comments open; comments and trackbacks closed. And that means … what looks like a mess of IF statements.

It can be daunting. The code is commented but remember to look for the blocks of IF and ELSEIF statements and you’ll be fine.

We also want to print a link to our permalink here for bookmarking purposes and the RSS for this particular single post—useful for tracking developing conversations.

					<div class="entry-utility">
					<?php printf( __( 'This entry was posted in %1$s%2$s. Bookmark the <a href="%3$s" title="Permalink to %4$s" rel="bookmark">permalink</a>. Follow any comments here with the <a href="%5$s" title="Comments RSS to %4$s" rel="alternate" type="application/rss+xml">RSS feed for this post</a>.', 'your-theme' ),
						get_the_category_list(', '),
						get_the_tag_list( __( ' and tagged ', 'your-theme' ), ', ', '' ),
						get_permalink(),
						the_title_attribute('echo=0'),
						comments_rss() ) ?>

<?php if ( ('open' == $post->comment_status) &amp;&amp; ('open' == $post->ping_status) ) : // Comments and trackbacks open ?>
						<?php printf( __( '<a class="comment-link" href="#respond" title="Post a comment">Post a comment</a> or leave a trackback: <a class="trackback-link" href="%s" title="Trackback URL for your post" rel="trackback">Trackback URL</a>.', 'your-theme' ), get_trackback_url() ) ?>
<?php elseif ( !('open' == $post->comment_status) &amp;&amp; ('open' == $post->ping_status) ) : // Only trackbacks open ?>
						<?php printf( __( 'Comments are closed, but you can leave a trackback: <a class="trackback-link" href="%s" title="Trackback URL for your post" rel="trackback">Trackback URL</a>.', 'your-theme' ), get_trackback_url() ) ?>
<?php elseif ( ('open' == $post->comment_status) &amp;&amp; !('open' == $post->ping_status) ) : // Only comments open ?>
						<?php _e( 'Trackbacks are closed, but you can <a class="comment-link" href="#respond" title="Post a comment">post a comment</a>.', 'your-theme' ) ?>
<?php elseif ( !('open' == $post->comment_status) &amp;&amp; !('open' == $post->ping_status) ) : // Comments and trackbacks closed ?>
						<?php _e( 'Both comments and trackbacks are currently closed.', 'your-theme' ) ?>
<?php endif; ?>
<?php edit_post_link( __( 'Edit', 'your-theme' ), "nttttt<span class="edit-link">", "</span>" ) ?>
					</div><!-- .entry-utility -->

That wasn’t so bad was it?

Single Post Content

Unlike index.php, single.php content is pretty simple. Just one plain function call followed by wp_link_pages().

<?php the_content(); ?>
<?php wp_link_pages('before=<div class="page-link">' . __( 'Pages:', 'your-theme' ) . '&amp;after=</div>') ?>

Post Attachments

Not a lot of people use post attachments but they’re kinda interesting. When you add an image to your post you’re actually attaching it to the post. And, of course, you can attach more than just images. We’re going to make an attachment.php template but you can, if you like, adapt it further to cover other types of attachments like video, audio, and applications, by making video.php, audio.php, and application.php templates. There’s lots of different ways to be creative with attachment templates and WordPress.

The easiest way to proceed here is by copying single.php, renaming it attachment.php, and making the following changes.

First of all, delete the top navigation. We won’t need it at all here. Replace it with a page title that links back to your parent post.

				<h1 class="page-title"><a href="<?php echo get_permalink($post->post_parent) ?>" title="<?php printf( __( 'Return to %s', 'your-theme' ), wp_specialchars( get_the_title($post->post_parent), 1 ) ) ?>" rev="attachment"><span class="meta-nav">&amp;laquo; </span><?php echo get_the_title($post->post_parent) ?></a></h1>

Since the page title is now wrapped in h1 tags that means our post title should be wrapped in h2 tags.

					<h2 class="entry-title"><?php the_title(); ?></h2>

Now, because our attachment template needs to actually show the attachment, our content needs to reflect that. And since most attachments are going to be images, we’ll want to check for that and cover that scenario with an IF statement.

					<div class="entry-content">
						<div class="entry-attachment">
<?php if ( wp_attachment_is_image( $post->id ) ) : $att_image = wp_get_attachment_image_src( $post->id, "medium"); ?>
						<p class="attachment"><a href="<?php echo wp_get_attachment_url($post->id); ?>" title="<?php the_title(); ?>" rel="attachment"><img src="<?php echo $att_image[0];?>" width="<?php echo $att_image[1];?>" height="<?php echo $att_image[2];?>"  class="attachment-medium" alt="<?php $post->post_excerpt; ?>" /></a>
						</p>
<?php else : ?>
						<a href="<?php echo wp_get_attachment_url($post->ID) ?>" title="<?php echo wp_specialchars( get_the_title($post->ID), 1 ) ?>" rel="attachment"><?php echo basename($post->guid) ?></a>
<?php endif; ?>
						</div>
						<div class="entry-caption"><?php if ( !empty($post->post_excerpt) ) the_excerpt() ?></div>

<?php the_content( __( 'Continue reading <span class="meta-nav">&amp;raquo;</span>', 'your-theme' )  ); ?>
<?php wp_link_pages('before=<div class="page-link">' . __( 'Pages:', 'your-theme' ) . '&amp;after=</div>') ?>

					</div><!-- .entry-content -->

Delete the bottom navigation from what was once your old single.php, and you’re done your attachment.php Template.

The 404 Template

A 404 Error is the server code for, “I can’t find this page” and it’s an event you need to take care of in your WordPress Themes. What happens when a link to your blog has a post url typed incorrectly? Or you unpublish a blog post? Your server coughs up a 404 error.

Luckily, WordPress has a template for that. It’s called, 404.php. The technique I stick with for 404 Templates is pretty straightforward but it works. Apologize and include a search form. There might be more creative solutions but none that get out of your visitor’s way faster.

Go back to your template-template above, drop the navigation and add something like this to the content.

				<div id="post-0" class="post error404 not-found">
					<h1 class="entry-title"><?php _e( 'Not Found', 'your-theme' ); ?></h1>
					<div class="entry-content">
						<p><?php _e( 'Apologies, but we were unable to find what you were looking for. Perhaps searching will help.', 'your-theme' ); ?></p>
	<?php get_search_form(); ?>
					</div><!-- .entry-content -->
				</div><!-- #post-0 -->

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

The WordPress Theme Index 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 Index Template. It’s part of The ThemeShaper WordPress Theme Tutorial: 2nd Edition.

Index.php is the most crucial WordPress Theme Template. Not only because WordPress needs to use it if you’re missing any of it’s brother and sister templates (like, category.php or tag.php) but because the work we do here, getting this template right, will help us breeze through the rest of our templates (with the exception of the dreaded comments template; that’s just plain difficult no matter how you look at it).

The Loop

Even though it’s stuck right in the middle of your template, in a metaphorical sense index.php begins and ends with The Loop. Without it you don’t have anything. Here’s what it looks like.

<?php while ( have_posts() ) : the_post() ?>
<?php endwhile; ?>

Simple really. And not even deceptively simple. While you’ve got posts in your database your theme will loop through them and for each one, do something. Getting the “do something” just right is the tricky part. But even that can be simple.

Try out this loop to get started and we’ll work on building it up. Put the following code inside your #content div in index.php.

<?php while ( have_posts() ) : the_post() ?>
<?php the_content(); ?>
<?php endwhile; ?>

What do you get if you do that? All the post content in a big smushed up pile. But it could be different.

<ul>
<?php while ( have_posts() ) : the_post() ?>
<li>
    <?php the_excerpt(); ?>
</li>
<?php endwhile; ?>
</ul>

Do you see what we just did there? Now you’ve got an unordered list of post excerpts. (Plus, now you can see what the_content() and the_excerpt() do)

Basically, you make a loop (starts with while ends with endwhile) and put some stuff in it—stuff being WordPress Template Tags that pull information out of the posts we’re looping though, just like bloginfo() pulled information out of our WordPress settings in the last lesson.

Alright, let’s make a really awesome loop. Let’s start with our basic, smushed up one. But we’ll make sure it’s ready for the More Tag and the Next Page Tag. We’ll also put it in it’s own div and let machines know it’s the content of a blog post with the microformat class “entry-content”.

					<div class="entry-content">
<?php the_content( __( 'Continue reading <span class="meta-nav">&amp;raquo;</span>', 'your-theme' )  ); ?>
<?php wp_link_pages('before=<div class="page-link">' . __( 'Pages:', 'your-theme' ) . '&amp;after=</div>') ?>
					</div><!-- .entry-content -->

How about the post title? That’s pretty simple too. We’ll use the Template Tag the_title() to get the title of the post and wrap it in an a tag that links to the_permalink() (that’s the permanent link to any particular post). We’ll also add in a title attribute and another microformat (bookmark) that tells machines (like Google) that this is the permalink to a blog post. Try putting the following just above the .entry-content div.

					<h2 class="entry-title"><a href="<?php the_permalink(); ?>" title="<?php printf( __('Permalink to %s', 'your-theme'), the_title_attribute('echo=0') ); ?>" rel="bookmark"><?php the_title(); ?></a></h2>

Now for all the extra bits that attend to any blog post: who wrote it, the time it was published, categories, tags, comments links. I like to break all this up into two sections: the meta stuff (author and entry date) which I put before the post content, and the utility stuff (categories, tags and comments link) that I put after the content. And both sections We’ll also put the post in it’s own containing div with the title.

Let’s take a look at the whole loop together. I’ve inserted some PHP comments in here to help guide you along.

<?php /* The Loop — with comments! */ ?>
<?php while ( have_posts() ) : the_post() ?>

<?php /* Create a div with a unique ID thanks to the_ID() and semantic classes with post_class() */ ?>
				<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<?php /* an h2 title */ ?>
					<h2 class="entry-title"><a href="<?php the_permalink(); ?>" title="<?php printf( __('Permalink to %s', 'your-theme'), the_title_attribute('echo=0') ); ?>" rel="bookmark"><?php the_title(); ?></a></h2>

<?php /* Microformatted, translatable post meta */ ?>
					<div class="entry-meta">
						<span class="meta-prep meta-prep-author"><?php _e('By ', 'your-theme'); ?></span>
						<span class="author vcard"><a class="url fn n" href="<?php echo get_author_link( false, $authordata->ID, $authordata->user_nicename ); ?>" title="<?php printf( __( 'View all posts by %s', 'your-theme' ), $authordata->display_name ); ?>"><?php the_author(); ?></a></span>
						<span class="meta-sep"> | </span>
						<span class="meta-prep meta-prep-entry-date"><?php _e('Published ', 'your-theme'); ?></span>
						<span class="entry-date"><abbr class="published" title="<?php the_time('Y-m-dTH:i:sO') ?>"><?php the_time( get_option( 'date_format' ) ); ?></abbr></span>
						<?php edit_post_link( __( 'Edit', 'your-theme' ), '<span class="meta-sep">|</span><span class="edit-link">', '</span>' ); ?>
					</div><!-- .entry-meta -->

<?php /* The entry content */ ?>
					<div class="entry-content">
<?php the_content( __( 'Continue reading <span class="meta-nav">&amp;raquo;</span>', 'your-theme' )  ); ?>
<?php wp_link_pages('before=<div class="page-link">' . __( 'Pages:', 'your-theme' ) . '&amp;after=</div>') ?>
					</div><!-- .entry-content -->

<?php /* Microformatted category and tag links along with a comments link */ ?>
					<div class="entry-utility">
						<span class="cat-links"><span class="entry-utility-prep entry-utility-prep-cat-links"><?php _e( 'Posted in ', 'your-theme' ); ?></span><?php echo get_the_category_list(', '); ?></span>
						<span class="meta-sep"> | </span>
						<?php the_tags( '<span class="tag-links"><span class="entry-utility-prep entry-utility-prep-tag-links">' . __('Tagged ', 'your-theme' ) . '</span>', ", ", "</span><span class="meta-sep">|</span>" ) ?>
						<span class="comments-link"><?php comments_popup_link( __( 'Leave a comment', 'your-theme' ), __( '1 Comment', 'your-theme' ), __( '% Comments', 'your-theme' ) ) ?></span>
						<?php edit_post_link( __( 'Edit', 'your-theme' ), '<span class="meta-sep">|</span><span class="edit-link">', '</span>' ); ?>
					</div><!-- #entry-utility -->
				</div><!-- #post-<?php the_ID(); ?> -->

<?php /* Close up the post div and then end the loop with endwhile */ ?>

<?php endwhile; ?>

Navigation

Now we need a way to navigate back through our posts. We’ll do this with 2 WordPress Template Tags: next_posts_link() and previous_posts_link(). These 2 functions … they don’t do what you think they do. I think the WordPress codex says it best.

next posts link
This creates a link to the previous posts. Yes, it says “next posts,” but it’s named that just to confuse you.
previous posts link
This creates a link to the next posts. Yes, it says “previous posts,” but it’s named that just to confuse you.

Just like everything in index.php, post navigation needs to be given some careful thought when we’re building it for the first time because we’re going to wind up using it on almost every page in our blog.

I like to have post navigation above and below the content. Depending on how you use this code in any particular situation, you may not use it though. No matter, we can always hide it like so.

.single #nav-above {
    display:none;
}

That CSS will hide post navigation above the content on single posts.

We’ll also want to hide any navigation code IF there’s nothing to navigate too. That is, if, say, on a search, there are no older pages of posts, we don’t want to output any navigation code at all to the browser. We’ll do that by wrapping our code in the following statement:

<?php global $wp_query; $total_pages = $wp_query->max_num_pages; if ( $total_pages > 1 ) { ?>
<?php } ?>

What we’re doing is checking to see what the maximum number of pages is in any loop we’re going to be looking at and if the number of pages is greater than 1, we’ll output our navigation.

Alright, here’s the code we’ll need for your navigation, top and bottom, just before, and just after the loop.

<?php /* Top post navigation */ ?>
<?php global $wp_query; $total_pages = $wp_query->max_num_pages; if ( $total_pages > 1 ) { ?>
				<div id="nav-above" class="navigation">
					<div class="nav-previous"><?php next_posts_link(__( '<span class="meta-nav">&amp;laquo;</span> Older posts', 'your-theme' )) ?></div>
					<div class="nav-next"><?php previous_posts_link(__( 'Newer posts <span class="meta-nav">&amp;raquo;</span>', 'your-theme' )) ?></div>
				</div><!-- #nav-above -->
<?php } ?>

 

<?php /* Bottom post navigation */ ?>
<?php global $wp_query; $total_pages = $wp_query->max_num_pages; if ( $total_pages > 1 ) { ?>
				<div id="nav-below" class="navigation">
					<div class="nav-previous"><?php next_posts_link(__( '<span class="meta-nav">&amp;laquo;</span> Older posts', 'your-theme' )) ?></div>
					<div class="nav-next"><?php previous_posts_link(__( 'Newer posts <span class="meta-nav">&amp;raquo;</span>', 'your-theme' )) ?></div>
				</div><!-- #nav-below -->
<?php } ?>

One last thing and we’re done index.php. You’ll be itching to see what we can do with it, but for now we’re just going to put in the function call just before get_footer().

<?php get_sidebar(); ?>

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

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

WordPress Theme Template & Directory Structure

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 WordPress Theme Template & Directory Structure. It’s part of The ThemeShaper WordPress Theme Tutorial: 2nd Edition.

While the most minimal of WordPress Themes really only needs an index.php Template and a style.css file (or just the style file if it’s a Child Theme) most WordPress Themes need something a little more solid.

Our new minimal will include 6 files. Make a folder in wp-content/themes/ for your theme—for this tutorial I’ll be using “your-theme” but it can be whatever you want—and create the following files in that new folder (don’t worry, they’ll be blank until the next few steps).

index.php
header.php
sidebar.php
footer.php
functions.php
style.css
Now let’s open up the last file we created, style.css, in a text editor. The first thing we need to do is add a section at the top of this file bracketed by what are called CSS “comments” (these guys: /* and */). It’s here that we need to put the info that tells WordPress about your theme. Without it, your theme won’t show up in the themes panel.

/*
Theme Name: Your Theme
Theme URI: http://example.com/example/
Description: A search engine optimized website framework for WordPress.
Author: You
Author URI: http://example.com/
Version: 1.0
Tags: Comma-separated tags that describe your theme
.
Your theme can be your copyrighted work.
Like WordPress, this work is released under GNU General Public License, version 2 (GPL).
http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
.
*/

Something to note: a lot of this is optional. Really, you just need the Theme Name. But if you ever plan on releasing your theme, or if you’re making a custom theme for someone, you’ll want to start out including most, if not all, of the rest. At the very least, I want you to feel free to mess around with it.

Once you’ve got that done you can activate your theme and navigate to your test site. We’ve made the ultimate blank theme! Things should start to get interesting right about now.

Building In Your HTML Structure

Now we get to use our HTML structure from the previous lesson. But first a mini-lesson about WordPress and Templates.

WordPress really only needs 1 template file, index.php. We can, and will be adding a series of template files that can be used instead of index.php for certain situations (single posts, category pages, etc.), but at the very beginning, index.php is all we’ll need.

Now, index.php and all it’s related brothers and sisters (which we’ll get to) make the web pages we see in our browser. They’re files with some HTML and HTML-outputting-PHP but in the end they make web pages.

Let’s think of web pages like stories, something with a beginning, a middle, and an end. Well, when we write out our index.php file (and later our single.php, category.php, etc.) we’re going to concentrate only on the middle bit. But! We’re going to call in the beginning bit and the end bit. We may have to always be redoing our middles but we’re only going to do the beginning and end of each web page once.

Header.php and Footer.php

Get the HTML structure we worked on in the previous lesson and copy everything up to and including

into header.php and save it. It should look like this:

<html>
<head>
</head>
<body>
<div id="wrapper" class="hfeed">
	<div id="header">
		<div id="masthead">

			<div id="branding">
			</div><!-- #branding -->

			<div id="access">
			</div><!-- #access -->

		</div><!-- #masthead -->
	</div><!-- #header -->

	<div id="main">

Now, copy everything after, and including,

into footer.php. It should look like this:

	</div><!-- #main -->

	<div id="footer">
		<div id="colophon">

			<div id="site-info">
			</div><!-- #site-info -->

		</div><!-- #colophon -->
	</div><!-- #footer -->
</div><!-- #wrapper -->
</body>
</html>

Index.php

I bet you can guess what we have to do now. Copy everything from our HTML structure inside the #main div into index.php. It should look like this:

		<div id="container">

			<div id="content">
			</div><!-- #content -->

		</div><!-- #container -->

		<div id="primary" class="widget-area">
		</div><!-- #primary .widget-area -->

		<div id="secondary" class="widget-area">
		</div><!-- #secondary -->

With only two small additions we’ll have a perfectly invalid WordPress Theme but we’ll be on the right track. We need to call in the header and footer to your theme.

At the top of index.php, before anything else, add the following template tag.

<?php get_header(); ?>

I think it’s pretty obvious what this tag does. It gets the header. But while we’re here, take a good look at this template tag if you’re new to PHP. I want you to notice a few things. First, our PHP function call—get_header()—begins with . Secondly, while our call is only 1 line long it ends with a semi-colon. Small, but important stuff.

Alright! Can you guess what function call we’re going to put at the bottom of index.php?

<?php get_footer(); ?>

Yep. Now we’ve got our main file that WordPress looks for, index.php. It has all the middle bits of our web page, but the top calls in the beginning bits, and the bottom calls in the ending bits.

Reload your page in the browser and check out the source code (View > Page Source, in Firefox). Look! It’s your code!

You’re on your way to making your first WordPress Theme.

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.

WordPress Theme Tutorial Introduction
Theme Development Tools
Creating a Theme HTML Structure
Template and Directory Structure
The Header Template
The Index Template
The Single Post, Post Attachment, & 404 Templates
The Comments Template
The Search Template & The Page Template
The Archive, Author, Category & Tags Template
The Sidebar Template
Reset-Rebuild Theme CSS & Define Your Layouts

Creating a WordPress Theme HTML Structure

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 Creating a WordPress Theme HTML Structure. It’s part of The ThemeShaper WordPress Theme Tutorial: 2nd Edition.

Now we’re starting to get into the real meat of WordPress Theme development: coding the HTML structure.

The Goals of Any HTML Structure

When coding a web site you should have 2 goals in mind: lean code and meaningful code. That is, using as little markup (HTML tags) as possible and making sure that the markup is meaningful by using semantic class and ID names that refer to their content, not how they “look” (class=”widget-area” instead of class=”sidebar-left”).

Now, when coding a WordPress Theme (or any template for any Content Management System) a balance is going to have to be struck between lean markup, with very little structure, and what’s called Divitis; including too many unnecessary div elements in your code. In other words, too much meaningless structure.

You’ve probably seen the div tag before if you’ve looked at the code for a website or any WordPress Theme. Think of them as containers for HTML code—containers that are very handy for manipulating HTML code with CSS. Obviously we’re going to have some. But we don’t want too many or ones without meaning. And we want enough structure—using the div tag—that we can reuse our code for multiple blog and site layouts. We want to code something we can come back to and use again.

Continue reading “Creating a WordPress Theme HTML Structure”

WordPress Theme Development Tools

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 WordPress Theme Development Tools. It’s part of The ThemeShaper WordPress Theme Tutorial: 2nd Edition.

Before we get started building any WordPress Theme we’re going to need to get our development tools in place. In this post, we’ll run through the best of the best and build ourselves a cross-platform WordPress Theme test environment that would do a professional Theme developer proud.

A Local Test Server: XAMP or MAMP

The best place to develop your custom WordPress Theme is off the web, on your home computer. To do that though you’ll need to turn your computer into a “local server”, essentially approximating the program suite on a regular web server (Apache, MySQL and PHP). This means you can install WordPress on your home computer.

Installing these separate server programs can be technically challenging but luckily for us there are a couple of free programs that will install and manage all this for us.

If you’re on a Windows computer you’ll want to try out XAMP.

If you’re running a Mac you’ll want to download MAMP. It’s what I use and it does the trick.

Continue reading “WordPress Theme Development Tools”

How To Create a WordPress Theme: The Ultimate WordPress Theme Tutorial

Update: We’ve created a second edition of this popular tutorial! It contains updated code samples, coverage of the latest theme development techniques, and more. Check it out at The ThemeShaper WordPress Theme Tutorial: 2nd Edition.

In only 11 individual lessons this WordPress Theme Tutorial is going to show you how to build a powerful, up-to-date, WordPress Theme from scratch. As we go along I’ll explain what’s happening including (for better or worse) my thinking on certain techniques and why I’m choosing one path over another. Essentially, I’ll be teaching you everything you need to know about WordPress Theme development.

Skip to the Table of Contents.

tutorial-graphic-large

Continue reading “How To Create a WordPress Theme: The Ultimate WordPress Theme Tutorial”

Using Action Hooks in WordPress Child Themes

In this post we’ll review how to write a PHP function and go over the basic idea of how you can use Action Hooks in your WordPress Theme. We’ll take a look at a practical example of injecting a Welcome Blurb into your Theme without touching the existing code and we’ll also look at how to remove existing content being injected into Theme Hooks.

Packing Up A Function

Action hooks are in a lot of WordPress Themes nowadays. There’s a good reason for that but you’re probably wondering what the big deal is right? They’re such a big deal because firstly, they’re incredibly easy to use and secondly, because they’re extremely powerful.

If you want to get started with them we’re going to have to take a look at how to write a PHP function again. Don’t worry, we’ll keep it pretty simple.

Continue reading “Using Action Hooks in WordPress Child Themes”