The Perfect Blog Theme

What is the perfect blog theme? I’ve been thinking about it and I think I have the answer.

For the blogger: The perfect blog theme disappears into the background and doesn’t hinder their writing in any way—in fact, it encourages it.

For the reader: The perfect blog theme disappears into the background and doesn’t hinder their reading in any way—in fact, it encourages it.

Please note what I haven’t included in my answer. It’s important.

Know of any publicly released themes that fit my criteria? I’d really like to know about them. Let me know if you’ve found them, or just let me know your thoughts on the perfect blog theme by leaving a comment.

And don’t be afraid to argue! I’ve been known to be wrong!

Registering New Sidebars for Custom Page Templates The Smart Way

One of the cooler ideas for a new WordPress default Theme that’s come up has been the idea of including a not-so-blog-ish custom home page template with the Theme. Something optional that you could use if wanted your home page to look a little different. Maybe more of a traditional web site look (whatever that is) or a magazine look. Simply create a new page, assign this custom template to it, set it as your home page, and boom! New look.

I thought this was a great idea too. Especially if that custom page template was totally widgetized. Load up whatever dynamic content you want there with the Query Posts Widget or just use Text Widgets. The default styles of the new 2010 Theme can decide which of several widget areas is the ‘featured’ area and if you want something different—without wanting to change the markup—you can move things around with a Child Theme.

Great, right? Wrong.

You’d have to register new widget areas. Widget areas that would be confusingly unavailable if you weren’t using the custom home page template. Unless …

Here’s a really nifty function written up by Chris Gossmann that’ll check to see if a particular custom page template is active. Chris had to write a SQL query to get this to work so be thankful that he survived to tell us about it.

function is_pagetemplate_active($pagetemplate = '') {
	global $wpdb;
	$sql = "select meta_key from $wpdb->postmeta where meta_key like '_wp_page_template' and meta_value like '" . $pagetemplate . "'";
	$result = $wpdb->query($sql);
	if ($result) {
		return TRUE;
	} else {
		return FALSE;
	}
} // is_pagetemplate_active()

OK. Even by itself that function is kinda cool. There’s a few neat things you can do with it. Here’s one. Combine it with the following code for registering a new sidebar in the functions.php file of your WordPress Theme:

function test_template_widgets_init() {     
	if(is_pagetemplate_active('template-active.php')) {
               
		register_sidebar( array (
			'name' => 'Test Widget Area',
			'id' => 'test-widget-area',
			'before_widget' => '<li id="%1$s" class="widget-container %2$s">',
			'after_widget' => "</li>",
			'before_title' => '<h3 class="widget-title">',
			'after_title' => '</h3>',
		) );
	
	} // end test for active template
} // test_template_widgets_init()
add_action ( 'init' , 'test_template_widgets_init' );

Can you guess what that does? Using our new conditional function, is_pagetemplate_active(), we’re registering a new widget area only if, in this case, template-active.php is being used by one of our pages. Completely bypassing our earlier problem of widget areas potentially hanging around without a page for them to be displayed on.

What do you think? Pretty cool, huh? I see lots of really neat possibilities here for custom page templates in WordPress Themes and in Child Themes.

40+ Thematic Resources, Tutorials and Links

The post Build WordPress Sites Fast With the Thematic Theme Framework by Es Developed is a great resource for anyone wanting to get started with the Thematic Theme. It lists over 40 resources and tutorials that’ll get you where you want to be with rapid WordPress Theme development.

And there’s some great explanation behind why you’d want to use the Thematic Theme. Under the heading Don’t Touch That Theme:

To create your theme, you don’t actually edit any of Thematic’s theme files. Instead, you make changes using a separate child theme.

It’s really powerful since you’re not actually touching any part of Thematic. You just get a nice starting place, without worries about future Thematic updates overwriting your code edits.

You don’t have to rewrite a bunch of code all over again–you’re just adding the bits that you want to be different. This works very much like the custom styles on WordPress.com hosted blogs.

Make sure you check out Build WordPress Sites Fast With the Thematic Theme Framework.

How To Reset & Rebuild WordPress Theme CSS & Define Your Layouts

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 How To Reset & Rebuild WordPress Theme CSS & Define Your Layouts. It’s part of The ThemeShaper WordPress Theme Tutorial: 2nd Edition.

CSS can be tricky. It can also be incredibly easy. I had a lot of help getting my head wrapped around CSS when I was first starting out and I take great pleasure in helping others the same way I was first helped: with solid code examples to learn from.

Here we’re going to layout a WordPress Theme CSS development arsenal for you:

  • A stylesheet that resets default CSS across all web browsers and makes a sane standard we can work from
  • Another stylesheet that rebuilds our typographical foundations in a smart way
  • A stylesheet just for WordPress classes (keeping the first two pure so we can use them for non-WordPress projects)
  • A series of 6 fluid stylesheets that will create ALL the common blog and website layouts you expect—and each one ready to adapt into a fixed width layout.

All the code we’ll talk about is open-source, under the GPL, and browse-able at the Your Theme Project Page. View the raw source for any one of these files and copy-paste at your leisure.

First things first, make a “style” directory in your Theme folder. That’s where we’ll be putting everything. Your CSS quiver, as it were. Ready to hit the target?

Reset CSS

Our Reset CSS is adapted from Eric Meyer’s famous Reset CSS with some minor, minor changes. Basically what it does is take all the typographical defaults of every browser and … obliterates them. You’re left with a squashy, gray mess.

It’s beautiful.

What this does is equalize the rendering of every browser, leaving you free to ignore countless individual quirks particular to each one.

Using it is simple. Add the following lines to your style.css, at the very top, immediately after the initial comments section.

/* Reset default browser styles */
@import url('styles/reset.css');

Reload, your page and check it what reset.css does in multiple browsers (if you can). It’s wonderfully gross, isn’t it?

Rebuild CSS

Our Rebuild CSS is my own personal invention adapted from an early version of the Blueprint CSS typography stylesheet and refined in the Thematic Project. What it does is swing back some vertical rhythm in our pages, but in a really smart way.

What I’ve tried to do with this iteration of my typography-rebuild CSS is combine the best of both worlds for web typography: using Pixels for font height, with relative line-height for the main declaration on the body element, and Ems for all subsequent vertical margins (like for paragraphs and lists).

What does this mean? It’s really easy to set your font height later—without doing any math work—and have all of your typographical elements follow suit with an appropriate vertical rhythm (the vertical space between type elements like paragraphs and lists).

Using rebuild.css is also really easy. Just add the following lines immediately after your reset.css import.

/* Rebuild default browser styles */
@import url('styles/rebuild.css');

The Basic WordPress Styles

There are some elements in WordPress that you’re just going to have to style every time. What I’ve done is taken those styles and put them in there own little corner called wp.css.

Right now, what we’ve got in there are styles for floating all the images—including handling captions and image galleries. And! preset styles for simple pull-quotes. All you need to do is add a class of left or right to the blockquote tag and you’re ready to roll.

Can you guess how we’re going to use wp.css?

/* Basic WordPress Styles */
@import url('styles/wp.css');

All The Layouts You’ll Ever Need

For your new theme, I’ve taken the rock-solid, indestructible layouts that shipped with the Sandbox Theme and adapted them for your new HTML structure. There are 6 in total. Each is a fluid layout (that stretches to fill the width of your browser window) but each one is easily adaptable to a fixed width layout.

Using anyone of these layouts is simple. Immediately after your basic WordPress styles import, import one of these layouts. Here’s how to import the 3 column layout, with the content in the center.

/* Import a basic layout */
@import url('styles/3c-b.css');

The simplest method of turning any one of these layouts into a fixed-width layout is to add a width and centering margin to the #wrapper div.

#wrapper {
  margin: 0 auto;
  width: 960px;
}

Bonus: Styling The Menu

If you’ve never taken an unordered list (that’s the smart markup generated by wp_page_menu) and styled it to look like a menu before it can seem kinda weird. As a bonus, here’s the CSS I use when I start out creating menus for WordPress Themes.

#access {
	margin: 0 0 1.5em 0;
	overflow: auto;
}
.skip-link {
	position:absolute;
        left:-9000px;
}
.menu ul {
	list-style: none;
	margin: 0;
}
.menu ul ul {
	display: none;
}
.menu li {
	display: inline;
}
.menu a {
	display: block;
	float: left;
}

It’s pretty simple but it’ll put you on sure footing. Good luck!

How To Create a WordPress Theme

This post concludes the WordPress Themes Tutorial series that shows 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

If you have any suggestions for posts that will fit in this series or complement what we’ve done so far I’d be glad to hear them. Let me know in the comments.

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

I know you’ve been waiting patiently for this one. Everybody loves the Sidebar Template. But we’re going to do ours a little differently than everyone else.

Ours is going to be better.

Custom Sidebar Functions

First things first. With a WordPress Sidebar Template, we need to make sure it’s widgetized. Ours is going to have two widget areas. That way we can re-use this code for 2-column or 3-column themes (on a 2-column theme the sidebars are stacked, one on top of the other).

This is pretty straightforward. In our functions.php file we’re going to register our widget areas with the following code.

// Register widgetized areas
function theme_widgets_init() {
	// Area 1
	register_sidebar( array (
		'name'          => 'Primary Widget Area',
		'id'            => 'primary_widget_area',
		'before_widget' => '<li id="%1$s" class="widget-container %2$s">',
		'after_widget'  => '</li>',
		'before_title'  => '<h3 class="widget-title">',
		'after_title'   => '</h3>',
  	) );

	// Area 2
	register_sidebar( array (
		'name'          => 'Secondary Widget Area',
		'id'            => 'secondary_widget_area',
		'before_widget' => '<li id="%1$s" class="widget-container %2$s">',
		'after_widget'  => '</li>',
		'before_title'  => '<h3 class="widget-title">',
		'after_title'   => '</h3>',
  	) );
} // end theme_widgets_init

add_action( 'init', 'theme_widgets_init' );

Now we’ve got two widget areas: Primary Widget Area and Secondary Widget Area. There’s no point naming them Primary Sidebar or Secondary Sidebar. In some layouts they might not even be sidebars—but they’ll always be widget areas.

Now, still in functions.php we’re going to add two more super-cool custom code snippets.

Firstly, we’re going to pre-set our default widgets: The Search, Pages, Categories, Archives, Links and Meta Widgets. We won’t be coding them in manually to sidebar.php. We’ll be telling WordPress to add them to our dynamic widget area in the settings (thank Ptah Dunbar for this).

$preset_widgets = array (
	'primary_widget_area'   => array( 'search', 'pages', 'categories', 'archives' ),
	'secondary_widget_area' => array( 'links', 'meta' )
);
if ( isset( $_GET['activated'] ) ) {
	update_option( 'sidebars_widgets', $preset_widgets );
}
// update_option( 'sidebars_widgets', NULL );

Now, in our Primary Widget Area (primary_widget_area) we’ve got the Search Widget, the Pages Widget, the Categories Widget, and the Archives Widget. The Secondary Widget Area (secondary_widget_area) has the Links and Meta Widgets. They’re all loaded up there in our WordPress options, ready and waiting.

Did you see // update_option( 'sidebars_widgets', NULL ); in the last line? Uncomment that line if you need to reset your widgets for any reason. As I’m sure you can guess, NULL means no widgets.

Now secondly, we’re going to create a new conditional that will check to see if there are any widgets in a given widget area. This will be incredibly useful (with props to Chaos Kaizer) when we code up our Sidebar Template.

// Check for static widgets in widget-ready areas
function is_sidebar_active( $index ){
	global $wp_registered_sidebars;

	$widgetcolums = wp_get_sidebars_widgets();

	if ( $widgetcolums[$index] ) return true;

	return false;
} // end is_sidebar_active

Now we need to put these custom code snippets to work.

Coding The Sidebar Template

With our dynamic widget areas registered and pre-set widgets, our Sidebar Template is going to be one of the simplest templates you’ll ever see. But remember, we’re also going to want to wrap our sidebars in an IF statement using our new conditional is_sidebar_active().

Here’s what it’ll look like:

<?php if ( is_sidebar_active('primary_widget_area') ) : ?>
		<div id="primary" class="widget-area">
			<ul class="xoxo">
				<?php dynamic_sidebar('primary_widget_area'); ?>
			</ul>
		</div><!-- #primary .widget-area -->
<?php endif; ?>

<?php if ( is_sidebar_active('secondary_widget_area') ) : ?>
		<div id="secondary" class="widget-area">
			<ul class="xoxo">
				<?php dynamic_sidebar('secondary_widget_area'); ?>
			</ul>
		</div><!-- #secondary .widget-area -->
<?php endif; ?>

Now if you go into the widget admin page and pull all those widgets out of any one of those widget areas the conditional statement guarding the markup will fail. Big time. And to our benefit. No widgets. No markup. Dead simple.

Just the way we like things around here.

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 Archive, Author, Category & Tags 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 Archive Template. It’s part of The ThemeShaper WordPress Theme Tutorial: 2nd Edition.

Much like we did with index.php, we’re going to get one master template done right and use it to build our other templates. Our master Template in this case is the Archive Template.

What archive.php does (and all it’s related templates) is show posts based on a select criteria. A date range, or posts by a certain author, a category, or a tag. So, basically, it’s a lot like index.php. If you can read the name of the template you can figure out what it’s going to spit out.

Let’s start again with our template-template from the previous lessons and build on top of it.

<?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(); ?>

The Archive Template

Here’s the scheme of an Archive Template:

Call the_post()
Check to see what kind of template this is
Produce an appropriate template
Rewind the posts with rewind_posts()
Do the usual loopy WordPress stuff
Here’s the #content of your archive.php Template. Note the Conditional Tags at the top for checking to see what kind of template we’re in.

<?php the_post(); ?>			

<?php if ( is_day() ) : ?>
				<h1 class="page-title"><?php printf( __( 'Daily Archives: <span>%s</span>', 'your-theme' ), get_the_time(get_option('date_format')) ) ?></h1>
<?php elseif ( is_month() ) : ?>
				<h1 class="page-title"><?php printf( __( 'Monthly Archives: <span>%s</span>', 'your-theme' ), get_the_time('F Y') ) ?></h1>
<?php elseif ( is_year() ) : ?>
				<h1 class="page-title"><?php printf( __( 'Yearly Archives: <span>%s</span>', 'your-theme' ), get_the_time('Y') ) ?></h1>
<?php elseif ( isset($_GET['paged']) &amp;&amp; !empty($_GET['paged']) ) : ?>
				<h1 class="page-title"><?php _e( 'Blog Archives', 'your-theme' ) ?></h1>
<?php endif; ?>

<?php rewind_posts(); ?>

<?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 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>

					<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 -->

					<div class="entry-summary">
<?php the_excerpt( __( 'Continue reading <span class="meta-nav">&amp;raquo;</span>', 'your-theme' )  ); ?>
					</div><!-- .entry-summary -->

					<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 -->
				</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">&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 } ?>

The Author Template

Not a lot is going to change with our Author Template. You’re going to like this one. Copy archive.php and rename it author.php. All we need to change is the page title section.

				<h1 class="page-title author"><?php printf( __( 'Author Archives: <span class="vcard">%s</span>', 'your-theme' ), "<a class='url fn n' href='$authordata->user_url' title='$authordata->display_name' rel='me'>$authordata->display_name</a>" ) ?></h1>
				<?php $authordesc = $authordata->user_description; if ( !empty($authordesc) ) echo apply_filters( 'archive_meta', '<div class="archive-meta">' . $authordesc . '</div>' ); ?>

Easy, right?

The Category Template

The Category Template is another simple template now that we have a proper Archive Template. Copy archive.php and rename it category.php.

Now open up functions.php. We’re going to drop a custom function—from the brilliant Sandbox Theme—in there that’s going to make our Category Template a little more usable.

// For category lists on category archives: Returns other categories except the current one (redundant)
function cats_meow($glue) {
	$current_cat = single_cat_title( '', false );
	$separator = "n";
	$cats = explode( $separator, get_the_category_list($separator) );
	foreach ( $cats as $i => $str ) {
		if ( strstr( $str, ">$current_cat<" ) ) {
			unset($cats[$i]);
			break;
		}
	}
	if ( empty($cats) )
		return false;

	return trim(join( $glue, $cats ));
} // end cats_meow

Our custom function cats_meow() removes the current category from category pages. In other words, it gets rid of redundant categories in that list of categories we have just underneath the excerpt of our post.

Now, back in category.php, replace the page title section with the following code:

				<h1 class="page-title"><?php _e( 'Category Archives:', 'your-theme' ) ?> <span><?php single_cat_title() ?></span></span></h1>
				<?php $categorydesc = category_description(); if ( !empty($categorydesc) ) echo apply_filters( 'archive_meta', '<div class="archive-meta">' . $categorydesc . '</div>' ); ?>

And in the .entry-utility div, replace …

						<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>

with the modified …

<?php if ( $cats_meow = cats_meow(', ') ) : // Returns categories other than the one queried ?>
						<span class="cat-links"><?php printf( __( 'Also posted in %s', 'your-theme' ), $cats_meow ) ?></span>
						<span class="meta-sep"> | </span>
<?php endif ?>

The Tags Template

The Tags Template is almost identical to the Category Template, except, well, it’s for Tags. You know the drill: copy archive.php and rename it tag.php.

We’ve also got a custom function—again, from the brilliant Sandbox Theme—for our functions.php called tag_ur_it(). It works just like cats_meow() except it removes redundant tags.

// For tag lists on tag archives: Returns other tags except the current one (redundant)
function tag_ur_it($glue) {
	$current_tag = single_tag_title( '', '',  false );
	$separator = "n";
	$tags = explode( $separator, get_the_tag_list( "", "$separator", "" ) );
	foreach ( $tags as $i => $str ) {
		if ( strstr( $str, ">$current_tag<" ) ) {
			unset($tags[$i]);
			break;
		}
	}
	if ( empty($tags) )
		return false;

	return trim(join( $glue, $tags ));
} // end tag_ur_it

Now, in tag.php, replace your page title with:

				<h1 class="page-title"><?php _e( 'Tag Archives:', 'your-theme' ) ?> <span><?php single_tag_title() ?></span></h1>

and in .entry-utility, replace …

						<?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" ) ?>

with the modified …

<?php if ( $tag_ur_it = tag_ur_it(', ') ) : // Returns tags other than the one queried ?>
						<span class="tag-links"><?php printf( __( 'Also tagged %s', 'your-theme' ), $tag_ur_it ) ?></span>
<?php endif; ?>

And that’s it!

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

The WordPress Theme Search Template and Page 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 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

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