Developing Your First WordPress Theme: Day 2 of 3
basix

Developing Your First WordPress Theme: Day 2 of 3

Tutorial Details
  • Program: HTML/CSS Editor of choice
  • Difficulty: Medium
  • Estimated Completion Time: 3 hours
This entry is part 2 of 3 in the series Developing Your First WordPress Theme

Themes are a huge part of what makes WordPress as popular as it is, and in this three-part series we’ll take you through a step-by-step process that results in a completed, functioning WordPress theme. In part two, we delve into coding the bulk of our theme!


Part Two

Welcome to part two of this three-part series on developing a WordPress theme from scratch. Last time we looked at what makes a “good theme”, as well as the general components required for a theme to work. This week we’re going to dive straight in and start creating our very first theme.


Our Theme

I’d like to stress that if you have your own layout to work with, use it – chances are you’ll learn more by experimenting and you’ll have a result that will be tailored to your tastes. If you don’t have a layout to work with, we’re providing a simple personal layout for you to work with.

This layout is something I made a while back as one of many unused designs for a friends personal blog. In the downloadable zip that accompanies this tutorial, you will find two folders; bftheme and bffiles. In the bffiles folder is the layout in its pre-WordPress state, the other folder contains the completed theme.

Open up the theme in a browser to get an idea of what we’re going for, it should look a little something like this:


Breaking It Up

While it would be possible to make a theme work with just one file, it’s very bad practice and not something you’ll see at all in the wild. It’s good to stick to the standard file structure as it makes theme modification easier down the line and lets you use the built in WordPress functions to include files easily (such as get_header()).

First of all, let’s prepare our workspace. In the themes directory of your WordPress installation, create a new folder called blindfaith. Inside that folder, create the following files:

  • index.php
  • single.php
  • page.php
  • comments.php
  • functions.php
  • header.php
  • footer.php
  • sidebar.php

Now we’ve got our stylesheets and images in place as well a theme skeleton, we’re going to jump right in. First we need to make our theme appear in the WordPress dashboard – to do this we need to modify our style.css file.

WordPress will automatically look for a file called style.css in a theme directory to extract information about the theme. Paste the following code in to the top of style.css:

/*
Theme Name: Blind Faith
Theme URI: http://www.danwalker.com/
Description: A minimalistic, simple theme for WordPress -- supports widgets in the sidebar, all manors of post/page formatting and comes with an easy to customize stylesheet. 

Author: Dan Walker
Version: 1.0
Tags: minimalistic, simple, widgets, sidebar, elegant
*/

If we check WordPress now, our theme should appear in the change themes part of the dashboard. Activate our new theme and load up the front page of WordPress. Seeing nothing? Good. Once WordPress has done its magic figuring out what page the user wants and has prepared the results, it calls index.php from our theme directory – except right now that file is blank.

First of all, we need to cut up our HTML in to its four respective parts; header, body, sidebar, footer. If you open up the index.html supplied with this tutorial, you should see this:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang='en'>
	<head>
	<title>Blind Faith</title>
	<link rel="stylesheet" href="reset.css" type="text/css">
	<link rel="stylesheet" href="style.css" type="text/css">
	<!--[if IE]>
	<link rel="stylesheet" href="ie.css" type="text/css" />
	<![endif]-->
	</head>
	<body class="home blog">
	<div id="top-bar-tile">
		<div id="top-bar-content">
			<h1><a href="http://www.danwalker.com/themes/blindfaith">Blind Faith</a></h1>
			<span class="slogan">Just Another Theme by Dan Walker</span>
			<div id="search-box">
				<form method="get" id="searchform" action="" >
					<input type="text" value="Search..." onfocus="if(this.value == this.defaultValue) this.value = ''" name="s" id="s" />
				</form>
			</div><!-- search-box -->
		</div><!-- top-bar-content -->
	</div><!-- top-bar-tile -->
	<div id="nav-bar-tile">
			 <div class="nav-bar-content"><ul><li class="current_page_item"><a href="http://www.danwalker.com/themes/blindfaith/" title="Home">Home</a></li><li class="page_item page-item-2"><a href="http://www.danwalker.com/themes/blindfaith/?page_id=2" title="About">About</a></li><li class="page_item page-item-4"><a href="http://www.danwalker.com/themes/blindfaith/?page_id=4" title="Typography">Typography</a></li></ul></div>
	</div><!-- nav-bar-tile -->
	<div id="wrapper">
		<div id="content">

			<div class="post">
				<h1><a href="http://www.danwalker.com/themes/blindfaith/?p=1">Hello world!</a></h1>
				<div class="post-details">
					<div class="post-details-left">
						Posted on <strong>February 6, 2011</strong> by <span class="author">Dan</span> under <span class="author"><a href="http://www.danwalker.com/themes/blindfaith/?cat=1" title="View all posts in Uncategorized" rel="category">Uncategorized</a></span>
						</div>
						<div class="post-details-right">
						<span class="comment-count"><a href="http://www.danwalker.com/themes/blindfaith/?p=1#comments" title="Comment on Hello world!">1 Comment</a></span>
					</div>
				</div>

				<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam malesuada sem in orci ornare et porta nisi tristique. Integer sodales feugiat interdum. Nunc nec hendrerit velit. Praesent at dolor arcu. Nulla porttitor dui vel justo vehicula fringilla. Nunc condimentum justo ut nibh pharetra tempor. Morbi nulla nisl, blandit eu egestas vel, cursus eget justo. Nulla lectus ante, pellentesque tempor pretium eu, porttitor vitae mi. Nunc et dolor eget purus ultricies sollicitudin. Phasellus luctus tincidunt lobortis. Nunc ac aliquam leo. In tortor orci, auctor non condimentum ultricies, hendrerit et nunc. Nam neque est, laoreet at rhoncus vitae, porttitor at nulla. Maecenas dignissim sagittis massa non ultrices. Cras eros quam, ultrices eu iaculis eu, egestas eget justo. Vivamus diam sapien, volutpat eget luctus nec, lacinia non quam. Duis suscipit nunc eget neque congue pretium. Vestibulum non lectus ut quam tempus varius. Nunc a ligula non metus luctus molestie.</p>

				<p> <a href="#" class="more-link">Read More</a></p>

				<div class="dots"></div>
			</div><!-- post -->

			<div id="only-page">No newer/older posts</div>

			<div class="spacer"></div>
			<div class="dots"></div>

			<div id="footer">
				Copyright © 2011 <a href="http://www.danwalker.com">Dan Walker</a><br />
				Don't steal anything etc
			</div>

		</div><!-- content -->
		<div id="sidebar">
			<div class="sidebar-box">
				<span class="sidebar-title">a bit about me</span>
				<div class="dots"></div>
				<div class="textwidget">
					<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam malesuada sem in orci ornare et porta nisi tristique. Integer sodales feugiat interdum. Nunc nec hendrerit velit. Praesent at dolor arcu. Nulla porttitor dui vel justo vehicula fringilla. Nunc condimentum justo ut nibh pharetra tempor. Morbi nulla nisl, blandit eu egestas vel, cursus eget justo.</p>
				</div>
			</div>
			<div class="sidebar-box">
					<span class="sidebar-title">recent posts</span><div class="dots"></div>
				<ul>
				<li><a href="http://www.danwalker.com/themes/blindfaith/?p=1" title="Hello world!">Hello world!</a></li>
				</ul>
			</div>
		</div><!-- sidebar -->
		<div class="spacer"></div>
	</div><!-- wrapper -->
	</body>
</html>

If we load up this HTML in a browser at the moment, we will see the basic layout. Looking at this layout, the first thing we need to do is decide what the header should include. Since we want the title, search box and navigation at the top of every page, that’s what we will put in to our header file.

The main content area will be variable, meaning it could be index.php, page.php, etc. The sidebar is all the content outside of the content box to the right of the layout and the footer will simply be the copyright message. Our theme will be made up like this:

So, copy the code from index.html all the way from the doctype to the content division and paste it in to our header file, we should now have a header.php file that looks like this:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang='en'>
	<head>
	<title>Blind Faith</title>
	<link rel="stylesheet" href="reset.css" type="text/css">
	<link rel="stylesheet" href="style.css" type="text/css">
	<!--[if IE]>
	<link rel="stylesheet" href="ie.css" type="text/css" />
	<![endif]-->
	</head>
	<body class="home blog">
	<div id="top-bar-tile">
		<div id="top-bar-content">
			<h1><a href="http://www.danwalker.com/themes/blindfaith">Blind Faith</a></h1>
			<span class="slogan">Just Another Theme by Dan Walker</span>
			<div id="search-box">
				<form method="get" id="searchform" action="" >
					<input type="text" value="Search..." onfocus="if(this.value == this.defaultValue) this.value = ''" name="s" id="s" />
				</form>
			</div><!-- search-box -->
		</div><!-- top-bar-content -->
	</div><!-- top-bar-tile -->
	<div id="nav-bar-tile">
			 <div class="nav-bar-content"><ul><li class="current_page_item"><a href="http://www.danwalker.com/themes/blindfaith/" title="Home">Home</a></li><li class="page_item page-item-2"><a href="http://www.danwalker.com/themes/blindfaith/?page_id=2" title="About">About</a></li><li class="page_item page-item-4"><a href="http://www.danwalker.com/themes/blindfaith/?page_id=4" title="Typography">Typography</a></li></ul></div>
	</div><!-- nav-bar-tile -->
	<div id="wrapper">
		<div id="content">

For now, we will skip the content section as that is the biggest and most complex part (made up of many different files) and move straight along to the footer. Usually a footer comes last in a layout, however since the sidebar comes after the footer in our code, we will simply call sidebar after footer. Therefore footer.php should contain the following code;

<div class="spacer"></div>

			<div id="footer">
				Copyright © 2011 Blind Faith by <a href="http://www.danwalker.com">Dan Walker</a><br />
			</div>

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

You’ll notice that we’re using our first WordPress function above; get_sidebar();. This function is roughly the same as require(“sidebar.php”); and since we’re going to always require the sidebar after the footer, it makes sense to call the sidebar in the footer file at the end.

Finally we need to include the sidebar. Since the footer comes before the sidebar, the sidebar is the last file included and so should close the design too (by closing the remaining tags; wrapper, body and html).

		<div id="sidebar">
			<div class="sidebar-box">
				<span class="sidebar-title">a bit about me</span>
				<div class="dots"></div>
				<div class="textwidget">
					<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam malesuada sem in orci ornare et porta nisi tristique. Integer sodales feugiat interdum. Nunc nec hendrerit velit. Praesent at dolor arcu. Nulla porttitor dui vel justo vehicula fringilla. Nunc condimentum justo ut nibh pharetra tempor. Morbi nulla nisl, blandit eu egestas vel, cursus eget justo.</p>
				</div>
			</div>
			<div class="sidebar-box">
					<span class="sidebar-title">recent posts</span><div class="dots"></div>
				<ul>
				<li><a href="http://www.danwalker.com/themes/blindfaith/?p=1" title="Hello world!">Hello world!</a></li>
				</ul>
			</div>
		</div><!-- sidebar -->
		<div class="spacer"></div>
	</div><!-- wrapper -->
	</body>
</html>

For now, open up index.php and enter the following;

<?php get_header(); ?>

<?php get_footer(); ?>

If we navigate to our installation of WordPress, we should get something like this

At the moment, the website content (or what we’ve done so far) seems to be loading but the stylesheets are not. This is because in our header file, the calls to the stylesheets now use a path that does not exist. They now reside in /wp-content/themes/blindfaith/, but there’s an easier way of pasting that everywhere it’s needed – a more dynamic way.


The bloginfo() Function

WordPress comes with a built in function called bloginfo() that’s great for getting all sorts of useful local information such as; blog name, description, stylesheet URL, stylesheet directory and more. For more information on what we can get with bloginfo, read this page in the WordPress codex.

Let’s open up our header file and change a few things to use the dynamic information provided by WordPress rather than the static information in the theme. Our current header.php file looks like this up until the nav-bar-tile tag:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang='en'>
	<head>
	<title>Blind Faith</title>
	<link rel="stylesheet" href="reset.css" type="text/css">
	<link rel="stylesheet" href="style.css" type="text/css">
	<!--[if IE]>
	<link rel="stylesheet" href="ie.css" type="text/css" />
	<![endif]-->
	</head>
	<body class="home blog">
	<div id="top-bar-tile">
		<div id="top-bar-content">
			<h1><a href="http://www.danwalker.com/themes/blindfaith">Blind Faith</a></h1>
			<span class="slogan">Just Another Theme by Dan Walker</span>
			<div id="search-box">
				<form method="get" id="searchform" action="" >
					<input type="text" value="Search..." onfocus="if(this.value == this.defaultValue) this.value = ''" name="s" id="s" />
				</form>
			</div><!-- search-box -->
		</div><!-- top-bar-content -->
	</div><!-- top-bar-tile -->

First of all, let’s fix the missing stylesheets. We’ll use two values from bloginfo; stylesheet_directory and stylesheet_url. The URL value is a direct link to style.css, whereas directory lists the directory style.css resides in. It’s important to remember that WordPress functions like this do not append a trailing slash to values.

Replace the stylesheet lines with the following;

<link rel="stylesheet" href="<?php bloginfo('stylesheet_directory'); ?>/reset.css" type="text/css">
	<link rel="stylesheet" href="<?php bloginfo('stylesheet_url'); ?>" type="text/css">
	<!--[if IE]>
	<link rel="stylesheet" href="<?php bloginfo('stylesheet_directory'); ?>/ie.css" type="text/css" />
	<![endif]-->

Our theme now has properly linked stylesheets. Next up, whilst we’re in the header file, let’s change the blog title, slogan and link.

The <title> tag doesn’t simply have the bloginfo property that represents the name of the blog, we can also add another function; wp_title(). This function returns the title of the page or post currently being viewed, prepended with ». To turn off the » before the page name, simply change the parentheses of wp_title() to wp_title(“”, true);

<title><?php bloginfo('name'); ?> <?php wp_title(); ?></title>

This will produce something along the lines of;

Local Test Blog >> Hello World

Now we can simply change the logo/title text, slogan and URL using the following bloginfo() properties;

<h1><a href=""><?php bloginfo('name'); ?></a></h1>
<span class="slogan"><?php bloginfo('description'); ?></span>

Our page should now look something like this;

Now we’ll take a look at the index.php file. This is the file called by default and should be able to display lists of posts for the front page, categories, archives and search results – or an error message if none exist. Obviously each one of these different lists can contain variable amounts of posts, we can work with this by using something in WordPress known as “The Loop”.


The Loop

WordPress figures out what posts to display before the index file is executed — after all we are making a theme, and the theme comes last as it’s simply a way of displaying output. Using some in-built functions, we can avoid having to do complex PHP loops with constant checks for valid data – this aspect of WordPress theme development makes the process a lot easier and causes less headaches.

First of all, let’s look at our post structure;

		<div class="post">
			<h1><a href="http://www.danwalker.com/themes/blindfaith/?p=1">Hello world!</a></h1>
			<div class="post-details">
				<div class="post-details-left">
					Posted on <strong>February 6, 2011</strong> by <span class="author">Dan</span> under <span class="author"><a href="http://www.danwalker.com/themes/blindfaith/?cat=1" title="View all posts in Uncategorized" rel="category">Uncategorized</a></span>
					</div>
					<div class="post-details-right">
					<span class="comment-count"><a href="http://www.danwalker.com/themes/blindfaith/?p=1#comments" title="Comment on Hello world!">1 Comment</a></span>
				</div>
			</div>

			<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam malesuada sem in orci ornare et porta nisi tristique. Integer sodales feugiat interdum. Nunc nec hendrerit velit. Praesent at dolor arcu. Nulla porttitor dui vel justo vehicula fringilla. Nunc condimentum justo ut nibh pharetra tempor. Morbi nulla nisl, blandit eu egestas vel, cursus eget justo. Nulla lectus ante, pellentesque tempor pretium eu, porttitor vitae mi. Nunc et dolor eget purus ultricies sollicitudin. Phasellus luctus tincidunt lobortis. Nunc ac aliquam leo. In tortor orci, auctor non condimentum ultricies, hendrerit et nunc. Nam neque est, laoreet at rhoncus vitae, porttitor at nulla. Maecenas dignissim sagittis massa non ultrices. Cras eros quam, ultrices eu iaculis eu, egestas eget justo. Vivamus diam sapien, volutpat eget luctus nec, lacinia non quam. Duis suscipit nunc eget neque congue pretium. Vestibulum non lectus ut quam tempus varius. Nunc a ligula non metus luctus molestie.</p>

			<p> <a href="#" class="more-link">Read More</a></p>

			<div class="dots"></div>
		</div><!-- post -->

Since we know what the structure of a post looks like and we have a copy with dummy information, we can replace the dummy information with some functions that will return actual post values. There are several pieces of information we can display with each post, such as;

  • the_title
  • the_tags
  • the_ID
  • the_category
  • the_date
  • the_content
  • many more (which you can find at http://codex.wordpress.org/Template_Tags#Post_tags)

When any one of these functions is called within the loop, they display the correct information for the current post being looped through.

Using the link above (or just the functions supplied before the link), try to replace the dummy content on your own with the correct replacement functions before carrying on.

The correct code should look like this;

	<div class="post">
			<h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
			<div class="post-details">
				<div class="post-details-left">
					Posted on <strong><?php the_date(); ?></strong> by <span class="author"><?php the_author(); ?></span> under <span class="author"><?php the_category(', '); ?></span>
					</div>
					<div class="post-details-right">
	<?php edit_post_link('Edit', '<span class="comment-count">  ' , '</span>'); ?><span class="comment-count"><?php comments_popup_link('Leave a comment', '1 Comment', '% Comments'); ?></span>
	</div>
			</div>

			<?php if ( is_archive() || is_search() ) : // Only display excerpts for archives and search. ?>
					<?php the_excerpt(); ?>
			<?php else : ?>
					<?php the_content('Read More'); ?>
			<?php endif; ?>

			<div class="dots"></div>
		</div><!-- post -->

There are a few bits you may not have been able to do, let’s look at them.

First of all, when displaying categories, WordPress likes to print a default style using a list. To override this, we can provide our own separator, which in our case is simply a comma and a space.

<?php the_category(', '); ?

Next up to display an edit link, followed by how many comments a post has, we can use a function that creates the link and can output three different states for; no comments, single comment and multiple comments (where the percentage sign is the placeholder for the comment count).

<?php comments_popup_link('Leave a comment', '1 Comment', '% Comments'); ?>

Finally we have a simple if statement to decide what type of content to show. If we use the_excerpt() then only an excerpt of the content is shown, if we use the_content() then all of the content is shown (or until a <–more–> appears in the content). On a search or archive page, it isn’t important to show the full article – just enough to get the gist – so we use the_excerpt().

Now in order to make this code work, we have to supply it with posts, and to do that we need to put it inside the loop. Our index.php file should currently resemble this;

<?php get_header(); ?>

		<div class="post">
				<h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
				<div class="post-details">
					<div class="post-details-left">
						Posted on <strong><?php the_date(); ?></strong> by <span class="author"><?php the_author(); ?></span> under <span class="author"><?php the_category(', '); ?></span>
						</div>
						<div class="post-details-right">
		<?php edit_post_link('Edit', '<span class="comment-count">  ' , '</span>'); ?><span class="comment-count"><?php comments_popup_link('Leave a comment', '1 Comment', '% Comments'); ?></span>
		</div>
					</div>
				</div>

				<?php if ( is_archive() || is_search() ) : // Only display excerpts for archives and search. ?>
						<?php the_excerpt(); ?>
				<?php else : ?>
						<?php the_content('Read More'); ?>
				<?php endif; ?>

				<div class="dots"></div>
			</div><!-- post -->

<?php get_footer(); ?>

Put this code after the header function;

<?php if ( ! have_posts() ) : ?>
		<h1>Not Found</h1>
			<p>Apologies, but no results were found for the requested archive. Perhaps searching will help find a related post</p>
<?php endif; ?>

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

and this one just before the footer function;

<?php endwhile; ?>

Loading up the WordPress blog should now look something like this;

Now our theme will show a list of WordPress posts according to how many should be displayed (changed under general settings in Dashboard). But what if there are more posts? What if the blog contains 6 posts but only displays 5? To combat this we need to add simple next/previous or new/old links.

Of course there’s no point displaying these links if there are no newer or older posts to jump to as it’s misleading, therefore we will use the following code after the loop;

<?php if ( $wp_query->max_num_pages > 1 ) : ?>
		<div id="older-posts"><?php next_posts_link('Older Posts'); ?></div>
		<div id="newer-posts"><?php previous_posts_link('Newer Posts'); ?></div>
<?php else: ?>
		<div id="only-page">No newer/older posts</div>
<?php endif; ?>

The next_posts_link() and previous_posts_link() will only display when they are needed, using the text provided in the first parameter. Of course it is possible to remove the if statement and only use the following two lines;

<div id="older-posts"><?php next_posts_link('Older Posts'); ?></div>
<div id="newer-posts"><?php previous_posts_link('Newer Posts'); ?></div>

But using the if statement above, we have the option of showing a message if there are no pages to skip to, which can make things less confusing for the user (which is always a good thing).

Our completed index.php file should look like this;

<?php get_header(); ?>

<?php /* If there are no posts to display, such as an empty archive page */ ?>
<?php if ( ! have_posts() ) : ?>
		<h1>Not Found</h1>
			<p>Apologies, but no results were found for the requested archive. Perhaps searching will help find a related post</p>
<?php endif; ?>

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

<div class="post">
	<h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
	<div class="post-details">
		<div class="post-details-left">
		Posted on <strong><?php the_date(); ?></strong> by <span class="author"><?php the_author(); ?></span> under <span class="author"><?php the_category(', '); ?></span>
		</div>
		<div class="post-details-right">
		<?php edit_post_link('Edit', '<span class="comment-count">  ' , '</span>'); ?><span class="comment-count"><?php comments_popup_link('Leave a comment', '1 Comment', '% Comments'); ?></span>
		</div>
	</div>

	<?php if ( is_archive() || is_search() ) : // Only display excerpts for archives and search. ?>
			<?php the_excerpt(); ?>
	<?php else : ?>
			<?php the_content('Read More'); ?>
	<?php endif; ?>

	<div class="dots"></div>
</div><!-- post -->

<?php endwhile; ?>

<?php if ( $wp_query->max_num_pages > 1 ) : ?>
		<div id="older-posts"><?php next_posts_link('Older Posts'); ?></div>
		<div id="newer-posts"><?php previous_posts_link('Newer Posts'); ?></div>
<?php else: ?>
		<div id="only-page">No newer/older posts</div>
<?php endif; ?>

<div class="spacer"></div>
<?php get_footer(); ?>

Single Posts

We now have a working index, but now we need a way to display single posts. Due to the simplicity of our design, our single.php file and index.php are near identical, the difference being that we don’t need next/previous links in single.php but we do need comments. The code for single.php is as follows;

<?php get_header(); ?>

<?php /* If there are no posts to display, such as an empty archive page */ ?>
<?php if ( ! have_posts() ) : ?>
		<h1>Not Found</h1>
			<p>Apologies, but no results were found for the requested archive. Perhaps searching will help find a related post</p>
<?php endif; ?>

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

<div class="post">
	<h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
	<div class="post-details">
		<div class="post-details-left">
		Posted on <strong><?php the_date(); ?></strong> by <span class="author"><?php the_author(); ?></span> under <span class="author"><?php the_category(', '); ?></span>
		</div>
		<div class="post-details-right">
		<?php edit_post_link('Edit', '<span class="comment-count">  ' , '</span>'); ?><span class="comment-count"><?php comments_popup_link('Leave a comment', '1 Comment', '% Comments'); ?></span>
		</div>
	</div>

	<?php if ( is_archive() || is_search() ) : // Only display excerpts for archives and search. ?>
			<?php the_excerpt(); ?>
	<?php else : ?>
			<?php the_content('Read More'); ?>
	<?php endif; ?>

	<div class="dots"></div>
</div><!-- post -->

<div class="spacer"></div>

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

<?php endwhile; ?>

<div class="spacer"></div>
<?php get_footer(); ?>

As you can see, not much has changed except around the bottom of the file. We’ve simply removed the code for the links and added a new function called comments_template(). The parameters for this function are simply the file to load (comments.php if left blank or by default) and whether or not to sort comments by type (default to false).

As you’ve probably guessed, the file that handles comments is comments.php – it is kept in a separate file so we can use it on both single.php and page.php alike.


Page

The file for displaying a page is almost identical to the single post file with the exception that information about time of post, author and so on is removed. This means the page file looks like this;

<?php get_header(); ?>

<?php /* If there are no posts to display, such as an empty archive page */ ?>
<?php if ( ! have_posts() ) : ?>
		<h1>Not Found</h1>
			<p>Apologies, but no results were found for the requested archive. Perhaps searching will help find a related post</p>
<?php endif; ?>

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

<div class="post">
	<h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>

	<?php if ( is_archive() || is_search() ) : // Only display excerpts for archives and search. ?>
			<?php the_excerpt(); ?>
	<?php else : ?>
			<?php the_content('Read More'); ?>
	<?php endif; ?>

	<div class="dots"></div>
</div><!-- post -->

<div class="spacer"></div>

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

<?php endwhile; ?>

<div class="spacer"></div>
<?php get_footer(); ?>

Navigation

The final part we’ll be adding today is the navigation up top (as it’s so simple)! Open up header.php and replace the nav-bar-tile division with this;

<div id="nav-bar-tile">
		 <?php wp_nav_menu(array( 'menu' => 'mainnav', 'menu_class' => 'nav-bar-content', 'menu_id' => 'navigation', 'container' => false, 'theme_location' => 'primary-menu', 'show_home' => '1')); ?>
</div><!-- nav-bar-tile -->

The wp_nav_menu function spits out a list of pages, however it also takes arguments in the form of an array. The arguments we have supplied, in order, are;

  • menu – The desired menu
  • menu_class – The CSS class to be used
  • menu_id – The UL element ID
  • container – Whether or not to wrap the list
  • theme_location – The location of the menu within the theme (for multiple menus)
  • show_home – Whether or not to display a ‘Home’ link

The CSS we’re using is coded to adapt to the way WordPress spits out a navigation list, it’s important to consider how a list will be outputted in code by WordPress when coding a design.

Our WordPress installation should now look like this;


Next Time…

Next time we will look at adding a functional sidebar that’s widget ready, dynamic post types, custom fields and wrapping up the final few elements in the theme!

If there is anything you were unsure of in this post, or if you just have questions, feel free to leave a comment below. For those with questions or just those wanting to know more, the WordPress Codex is goldmine of information for all the functions and so on that WordPress uses.


Other parts in this series:Developing Your First WordPress Theme: Day 1 of 3Developing Your First WordPress Theme: Day 3 of 3
Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://wpfu.net wpfu

    This seems to be the same tutorial as that in net.tutplus but it was in video right?

    • Brandon Jones

      It’s similar, but we’ll end up re-treading a lot of what was on Nettuts over the next month or so as we add in these “staple” tutorials to the library here. You can also expect quite a bit of brand new content though, so there’ll be something for everyone. Thanks for the comment wpfu!

      • Lloyd

        Re tread till the cows come home I say.

  • picasso

    i thaught it was a videos tutorial.
    harm!!!!!!

  • Pingback: Learning Wordpress | Marketer Matt

  • http://ikaami.deviantart.com Ikaami

    Awesome tut, looking forward to reading more. You guys do a hell of a good work here.

  • Julien

    I think the workfiles are missing ?! Can you put them online please? Thanks for these series by the way ;)

    • Brandon Jones

      They should be up now Julien – let me know if you still can’t access them though :)

      • Julien

        Perfect :)

  • http://www.richardjohnashe.com Richard

    Recently I’ve been grappling with using $wpdb to submit a registration form. A really in depth tutorial on this would be great!

    Rich

    • http://unrelatedmedia.ca Neil Davidson

      Here, try this tutorial I wrote: Registeration Tricks

      While it was written specifically for the Members plugin by Justin Tadlock the principles of code still apply to any base registration. You request for a tutorial on the registration form is very general as there are so many things that can be done. Specifics, my man, specifics.

  • http://www.rouse.ws William Rouse

    “In the downloadable zip that accompanies this tutorial…”

    Where do we find the downloadable zip file?
    Thanks!
    WBR

    • http://www.stalsberg.net Ware

      First of all… Keep it going! Love this! And the way you write it, it’s also easy to understand, not too overly in depth, but still ppl should get the basic idea. Good job, and can’t wait for part 3.

      Though, as William Rouse mentions here…
      What downloadable .zip file? *loooks around*

      Ware

      • Ware

        Reloaded the page, and there it was.. :S
        Ohhh well, as said, great going! Keep it up!

  • http://varemenos.com/ Varemenos

    Thanks for the detailed tutorial, it was pretty good!

    I really hope in the next tutorial you will show us how to use multiple dynamic sidebars.

  • http://www.jvsoftware.com/ Javier Villanueva

    Moar HTML5 pleasee that doctype makes me sadface

    • Clayton Bellmor

      Haha. I was always feel the same way!

  • http://jameserie.info JmsRie

    Clean and simple theme, nice tut!

  • http://man-sur.blogspot.com Mansur

    Nice tutorial I followed step by step… and I am succeed…. till the comment template section. :(

    comments.php is blank file.. it only worked when I replaced with twentyten theme’s comments.php file.

    please add comment.php code on this tutorial… your download also not attached the proper comments.php…

    Please advice…

    Thank You.

  • http://www.jsxtech.com Jaspal Singh aka jsxtech

    works great!!!
    Thanks for sharing the tutorial.

  • http://www.jaymanpandya.com Jayman Pandya

    Super-awesome… I was waiting for this… Now its time to complete my website.

  • http://www.elcodigok.com.ar danyx

    Excelente este nuevo tutorial para crear un theme en wordpress.

    Saludos!

  • http://themeforest.net/user/jvanoel/portfolio Boba

    Cool series, keep it coming. :)

    Just a few things i would like to mention :)

    I would recommend placing the reset.css inside of style.css, placed at the top in a single line, one less file request so it’s a bit faster.

    And you used bloginfo(‘stylesheet_directory’); when you included the reset.css. That means if a child theme is used it’ll get the reset.css from the child theme, and there’s a big chance there won’t be one. Use get_template_directory_uri(); instead.

    Also your theme would get soft rejected on Themeforest for using bloginfo(‘stylesheet_url’); instead of get_stylesheet_uri(); and bloginfo(‘stylesheet_directory’); instead of get_stylesheet_directory_uri. Same goes for the “template” instead of “stylesheet” version.

    Instead of bloginfo(‘url’) i suggest home_url(); (usually in WordPress, functions that return a value start with “get_”, this one does not but it does return the value so needs echo).

    The loop part seems a bit odd :) I guess it works ok, but not sure since there’s no checking if there are posts before doing the loop. It’s usually:

    if (have_posts()) : while (have_posts()) : the_post();

    //loop posts

    endwhile; else:

    //no posts

    endif;

    That’s all :) Cheers.

    • Clayton Bellmor

      Thanks for the tips about Themeforest and the Loop!

    • neindesign.net

      Can you please explain? The official WordPress 3.2 theme ‘Twenty Eleven’ uses:

      href=”"

      Thank you

      • neindesign.net

        Fix:

        href=”"

    • http://webdesign.readygoplay.com Windo

      hey hey, slow down a sec.. lol

      noob question., so the different between get_bloginfo() and bloginfo() is that get_bloginfo() going to need echo to write it to the browser?

      quote from wordpress codex :

      ” Displays information about your blog, mostly gathered from the information you supply in your User Profile and General Options from the WordPress Administration panels (Settings → General). It can be used anywhere within a page template. This always prints a result to the browser. If you need the values for use in PHP, use get_bloginfo(). ”

      Thanks :D

    • Alex

      Yeah, those are good tips to know about themeforest

    • http://www.hyphenstudio.ca HyphenStudio

      Boba,

      Although I have to agree with most of what you say here – your recommendations are good. I have downloaded a few different themes over the past couple of months that use the ‘bloginfo’ tag for linking to stylesheets. This code is still valid and doesn’t seem to have caused any approval issues with even some of the newest themes being approved.

      That being said – what is the real advantage to using get_stylesheet_uri(); over bloginfo?

      Thanks

      Mike

  • http://www.skippednote.com Bassam Ismail

    Should there be a semi colon instead of colon ?

  • http://blog.kamarkecil.com Arif

    it’s great! i follow the steps..

  • http://arifriyanto.com/blog Arif Riyanto

    Yups, i wait the next…

  • http://- Maggy

    Hi,

    I just completed your “Developing Your First WordPress Theme: Day 2 of 3″.
    Just want to say it was great and inspiring, learned a lot, thanx!

    Also just wondering if you have any suggestion on how to build this kind of product javascript or spry-meny in wordpress?

    http://www.nordchark.se/v2/falukorv_basturokt.html

    How do i connect the text and images feilds?
    thru custom feils? It has to be easy to update the product info for the client.

    Keep up the great work.

    Best,
    Maggy

  • http://www.webguide4u.com Vivek Parmar

    Awesome, thanks for sharing this. waiting for next tutorial

    • http://www.bing.com/ Lorena

      Thanks for hepinlg me to see things in a different light.

  • http://www.espadilha.com Assad

    That`s amazing man, that`s all i needed !!!

  • http://www.rendertom.com Tomas Sinkunas

    Wow, looks awesome. I am no web developer, but always wanted to understand how to use WP. Great series.

    At the moment I am pretty busy at work, but after I find a minute, I’ll jump to this tutorial. I have no background in coding, so wonder if I need some extra stuff to follow this tutorial? Like a server or whatever? And what good software could you please advise? I am on MAC.

    I guess simple text editor will do, right? But maybe you could suggest something better for coding?

    Any ways, great series. Great new site. Keep it up!

    • Alex

      The best code editor on a mac is Coda ($99). It is made by panic software, who also have the best FTP client out there, Transmit ($40 – but cyberduck is a close second and its free). The nice thing with Coda is not only is it a great code editor, but it has FTP built right into it, plus all your syntax highlighting and auto completion which not only helps but saves you time typing commonly repeated lines of code. Coda also have a clip system in it, which allows you to save long series of code and access it with a single keyword. I have hundreds of clips but one example is a CSS Reset. I have a CSS reset in there (which is like 100 lines of code) and all I have to do is type “cssreset” and then press tab after it and it instantly inserts the whole reset in there, no copy paste or anything, saves a lot of time. It also has a preview window built in too (preview your site changes without switching to safari and refreshing), and a CSS editor for doing fast CSS edits visually. The last great thing about it is that there is a “books” tab where you can have reference books to access quickly in you need a reminder of what parameters are needed for a particular function, or to look up more information about a function or usage. I also have the wordpress codex on there, so I have access to the whole wordpress codex without leaving the program.

      I don’t belong to Panic, I just really like Coda. Its the best thing out there, but the price is not the easiest thing to swallow. http://www.panic.com/coda/

  • http://www.wpyag.com Ravi

    Great… A must read post for all WordPress lovers.

  • Clayton Bellmor

    Thanks for the writeup! Very informative.

    Great comments too.

  • Holden

    Great tutorial. Looking forward to part 3!

  • Fahad

    Hi,
    You did not mention how to setup functions in this tutorials as i am a beginner in php so i am stuck on how to setup those functions.
    Thank You.

    • Holden

      I’d really like to know how to utilize the wp_nav_menu function, so that the admin can adjust menu items in the ‘Appearance’ tab of the dashboard.

      • http://unrelatedmedia.ca Neil Davidson

        It is difficult to remember all things to add to a tutorial, especially if you actually know what you are doing.

        Experienced programmers often overlook things simply because it is common sense to them. This is where tutorial editors come in – preferable one who can code (to test the tutorial) and one who is not so proficient (to test comprehension level).

        Anyway, for your question about adding menus there are many tutorials available, but my favourite is Justin Tadlock’s

        Although slightly outdated it is current enough with the most recent version of WordPress and I don’t see it changing in the next few dozen WordPress updates.

        • Holden

          Neil, I completely understand. 95% of what I know is self-taught on a need basis, so I often come against really simple things that I’ve overlooked or were simply not an issue for the specific projects I was working on. I keep trying to make myself go back and learn things from the ground up, but it’s definitely a long road.

          Thanks very much for posting the other tutorial, I’ll check that out today!

  • Tim

    I would advise against closing HTML tag in sidebar. For certain pages in a custom theme you might not want to include a sidebar but that’s where you closed the HTML elements. I would consider re coding slightly to utilize wordpress template hierarchy to be expandable. Great writup though. Keep em coming!

  • Fahad

    Hi,
    I am using my design for WordPress theme and it has images but they don’t display on WordPress any idea on how to display images in WordPress?

    • magron

      i had same problem at first.. just change all the paths to “absolut path” instead “relative”.. At least for that ones wasn’t in CSS..

    • http://www.beerensvanierland.nl toon

      to display images the right way: create a folder ‘images’ in the wp-content/themes/yourtheme/
      put the images in this folder.

      In the header.php file put: <link rel="stylesheet" href="” type=”text/css”>

      In the style sheet (css) make a path to your images, for example: background:url(images/logo.png);

      Now just must see them.

      (this took me 3 2 days!!!)

      greetzz, Toon

    • http://www.beerensvanierland.nl toon

      Sorry!

      This is the right reply text :

      To display images the right way: create a folder ‘images’ in the wp-content/themes/yourtheme/

      Put your images in this folder. So that you have:

      wp-content/themes/yourtheme/images

      In the header.php file of your site, put:

      <link rel="stylesheet" href="” type=”text/css”>

      In the style sheet (style.css) make a path to your images, for example:

      background:url(images/logo.png);

      You see: ‘naming’ the folder is doing the trick.

      Now images are loading, because it’s the right path for WordPress to load them.

      (…Took me 2 days to figure this out. Since I find so much worthy tips and tutorials I assumed this is another good one to share…)

      Succes, Toon

    • http://www.beerensvanierland.nl toon

      Very strange,

      The programm is not showing my code line.

      Here it is aganin:

      header.php file, put:

      <link rel="stylesheet" href="” type=”text/css”>

      I assume it shows correctly.
      greetzzz, Toon

    • http://www.beerensvanierland.nl toon

      Last try! (grrrrr….)

      code in header.php is:

      code is:

      /pre>

      • http://www.beerensvanierland.nl toon

        Drive me nuts!

        last time: code in header.php is: ?php bloginfo(‘stylesheet_url’); ?

        <link rel="stylesheet" href=" (….. code….. in between ) ” type=”text/css”>

  • Holden

    Any idea when part 3 will be coming along?

  • http://livelyworks.net/ LivelyWorks

    Thanks for great Tutorial!!

  • http://www.daveismyname.co.uk Dave

    Great tutorial following on from what Boba said if your going to use get_stylesheet_uri() remember you need to echo it out like this:

    <link rel=”stylesheet” href=”<?php echo get_stylesheet_uri(); ?>” type=”text/css”>

  • pat

    can someone give my the video tutorial for building this WordPress theme cause i cant find the link to the video.

  • Laurel Pehrson

    I am having difficulty with the “Leave Comment” link. I can not get a space to go between “Leave Comment” and “Edit.” They are smashed together. When I click on Leave Comment it does nothing. I’m guessing it is not supposed to work at this point because we have not edited comments.php, correct?

    It looks like this:

    <?php edit_post_link(' Edit ', ' ‘ , ”); ?>

    Also, is supposed to make the blog posts on the index.php page display as excerpts at this point? I had one article that was 6 paragraphs long and it is displaying 100% of it on the home page.

    Any idea on how to resolve these two issues?

    • Laurel Pehrson

      Sorry, this is what I have for the edit and comments links:

      Posted on by under

      <?php edit_post_link('Edit', ' ‘ , ”); ?>

      • Kevin Mata

        Have you tried adding a class to the edit link and applying margin with CSS?

  • Pingback: Developing Your First WordPress Theme | Wptuts+

  • flashysite

    hi,

    Thanks for the tutorial.

    I’m very new to wp, but managed to install and got wp in my mamp. I can see TwentyTen them when activated.

    However, when following the tutorial, I couldn’t see anything in Blindfaith theme.

    I copy/paste the 4 php files. header.php , footer.php , sidebar.php , index.php

    Nothing show up when i click on “visit site” from the dashboard.

    Anyone knows what I’m doing wrong or missing?

    thank you for your kind reply

    • http://www.thehistoryofhalo.com Will

      you need a ‘style.css’ file too. downloadthe source filesat the top of the post :-)

      wordpress pulls it’s display info for themes from the style.css file(as well as the style for the theme obviously lol) like the theme name, author, dscription etc. the stuff that’s displayed when you go to the themes page in the wordpress dashboard

  • http://www.sagive.co.il Sagive SEO

    Great guide to clip and back for Copy & paste while building a new wp theme :) thanks

  • Pingback: Tema wordpress

  • trix

    A great tut. But the steps are far to big for someone who never touched the code behind a theme.
    I can handle HTML and CSS very well and I heard of all the template Tags before. Nevertheless it wasn’t easy for me to follow the steps. The reader has to do many things by himself that aren’t explained.

  • Ali

    This code works for wordpress 3.0+ right?

    Thanks!

  • http://www.metapenguins.com Heather

    Great tutorial! Ive been looking for something more descriptive and step based like this.

    The only thing that I wished it had more of was comment lines in the coding. I think that would have made it easier for some of the more “beginner” users. Nothing drastic, just simple things so the people know where to go for specific things.

    These are examples from my HEADER.PHP file






  • http://www.metapenguins.com Heather

    oops, forgot about the code snippet thing.repost

    Great tutorial! Ive been looking for something more descriptive and step based like this.

    The only thing that I wished it had more of was comment lines in the coding. I think that would have made it easier for some of the more “beginner” users. Nothing drastic, just simple things so the people know where to go for specific things.

    These are examples from my HEADER.PHP file
    -CALL THE DOCUMENTS INFORMATION
    -CALL THE STYLE SHEETS
    -START THE TOP HEADER BAR
    -CHANGE THE LOGO/TITLE TEXT, SLOGAN AND URL
    -ADD A SEARCH BAR
    -START THE NAV HEADER

  • http://www.thehistoryofhalo.com Will

    Finaly I found a decent explaination of the famous ‘wordpress loop’… and you weren’t even trying to explain! I’ve never been on this site before but I’m sure there is more knowledge hidden in the rest of these posts. I’ve tried and given up so many times to make my own unique theme but have never once made it to a finished product. Bonus is that I have dozens of unique HTML and CSS layouts to finally bring to life. I’m too tired to read part 3 of this post but I’m leaving it open so it’s the first thing I see tomorrow morning haha Ain’t even risking loosing this site in my masses of booksmarks lol

  • tide

    Hi, i downloaded the files and run it using wordpresss 3.2.. The NAVIGATIONS are not aligned together all in all.. Sample Page2 menu is doing a break line or next line.. In the example above, all of the menus in the navigation are aligned.. Anyone might know why is this happening?? Tnx.

  • Jack

    I decided to start developing a site based on this theme and I’m finding it doesn’t handle subpages in the menu area.

    Has anyone else run into this issue? If so, do you have a fix?

    Thanks so much in advance.

  • ron

    Hi,
    Is this one can be used as stater for a parent theme?..

    and why didn’t you suggested to change Theme Name,Theme URI,Description and Author on top of the page in the css.style file? so it would be the reader theme… :)

    Thanks
    ron

  • Azzoth

    functions.php doesn’t seems to work. Is it supposed to be linked somewhere ?

  • Nevin

    Hi,

    I can’t seem to find the zip file that I need to download. Whenever I click the Download button it just opens a blank screen in my browser. Any recommendations?

  • Pingback: Pecado's Blog » İlk WordPress Temanızı Geliştirin : Bölüm 2 » Pecado's Blog

  • Jenny

    Aaaaah, help, why am I not getting your layout after putting bloginfo() Function in place. I’m suspecting that I need to do something more on style.css…. My page still looked like a straight top-to-bottom page without much formatting -everything is left-aligned, even the side bar where it should go to the right side….. Is there something missing? Should I look into adding something in style.css besides just the description of the theme? Please HALP!!

  • Rishikant

    Thanks for this tutorial..

  • http://banglapdf.net sunny

    can any one help with this lines:

    <link rel="stylesheet" href="/reset.css” type=”text/css”>
    <link rel="stylesheet" href="” type=”text/css”>

    <link rel="stylesheet" href="/ie.css” type=”text/css” />

    it is not working for me…..I have tested it in local server……….so what should I put in (‘stylesheet_directory’) & (‘stylesheet_url’) to replace this ???

  • Anonymous

    If we navigate to our installation of WordPress, we should get something like this :

    Can someone help me why I can see anything ?. I have installed LOCALLY wordpress and other themes work. I just can’t see anything. I’ve checked everything . Tried more then five times . But still no luck :(

  • http://www.areawebcreativa.com Oscar

    Many thanks for the tutorial. It’s Great

  • BounasserAbdelwahab


    var hello = function() {
    return "great effort !!";
    };

  • Pingback: Xegai.ru » Developing Your First WordPress Theme: Day 3 of 3

  • apdubey

    Thanks a lot for all your effort… its realy a great tutorial for simple wordpress theme creation.

    I have been able to convert more than 4 html css files in to wordpress but i have few issue that i am not able to solve…not sure if you will reply or not as have not seen any of your reply in bellow comments….

    My problems are

    1) If there are more than one blog post in my theme theme get disturbed… depending on theme some time footer start from blog div instead of start corner, or UI get messy or some time like that … if only one blog post is there it works great..

    2) I am not able to widget enable theme.. even your given theme is not widget enabled so please suggest what i should do..

    Thanks a ton for all your help that you already gave and thanks in advance for future help.

    Regards
    AP Dubey

  • bestblinds

    This is a good post. This post give truly quality information.I’m definitely going to look into it.Really very useful tips are provided here.thank you so much.Keep up the good works.

  • Hiral

    Superb Tutorial.. Thanks a ton to author! :) it really helped.very easy to understand and implement…

  • w1234

    in my site, i have used plugins. so there are many pages at navigation bar in header. but it is breaking the design so please tell me a solution to do it.