Dissection of a WordPress theme: Part 4

A single post

As mentioned previously, WordPress delegates the responsibility for displaying information to different files. The front page is handled by index.php, and we have already styled this. If we now click on a post we discover a nasty secret:

Hidden secret

Not a very attractive style! The reason for this mess is because a single post is handled by single.php and not index.php. We haven’t looked at single.php yet, so let’s do that now.

<?php get_header(); ?>
 <div id="content" class="widecolumn">
 <?php if (have_posts()) : while (have_posts()) : the_post(); ?>

   <div class="navigation">
     <div class="alignleft"><?php previous_post('&laquo; %','','yes') ?></div>
     <div class="alignright"><?php next_post(' % &raquo;','','yes') ?></div>
   </div>

   <div class="post">
     <h2 id="post-<?php the_ID(); ?>"><a href="<?php echo get_permalink() ?>" rel="bookmark" title="Permanent Link: <?php the_title(); ?>"><?php the_title(); ?></a></h2>

     <div class="entrytext">
       <?php the_content('<p class="serif">Read the rest of this entry &raquo;</p>'); ?>
       <?php link_pages('<p><strong>Pages:</strong> ', '</p>', 'number'); ?>

       <p class="postmetadata alt">
         <small>
           This entry was posted
           <?php /* This is commented, because it requires a little adjusting sometimes.

             You'll need to download this plugin, and follow the instructions:
             http://binarybonsai.com/archives/2004/08/17/time-since-plugin/ */

             /* $entry_datetime = abs(strtotime($post->post_date) - (60*120)); echo time_since($entry_datetime); echo ' ago'; */ ?>
           on <?php the_time('l, F jS, Y') ?> at <?php the_time() ?>
           and is filed under <?php the_category(', ') ?>.
           You can follow any responses to this entry through the <?php comments_rss_link('RSS 2.0'); ?> feed.
          
           <?php if (('open' == $post-> comment_status) && ('open' == $post->ping_status)) {
             // Both Comments and Pings are open ?>
             You can <a href="#respond">leave a response</a>, or <a href="<?php trackback_url(display); ?>">trackback</a> from your own site.
          
           <?php } elseif (!('open' == $post-> comment_status) && ('open' == $post->ping_status)) {
             // Only Pings are Open ?>
             Responses are currently closed, but you can <a href="<?php trackback_url(display); ?> ">trackback</a> from your own site.
          
           <?php } elseif (('open' == $post-> comment_status) && !('open' == $post->ping_status)) {
             // Comments are open, Pings are not ?>
             You can skip to the end and leave a response. Pinging is currently not allowed.
    
           <?php } elseif (!('open' == $post-> comment_status) && !('open' == $post->ping_status)) {
             // Neither Comments, nor Pings are open ?>
             Both comments and pings are currently closed.     
          
           <?php } edit_post_link('Edit this entry.','',''); ?>
         </small>
       </p>
     </div>
   </div>
  
 <?php comments_template(); ?>

 <?php endwhile; else: ?>
   <p><?php _e('Sorry, no posts matched your criteria.'); ?></p>
	<?php endif; ?>
</div>

<?php get_footer(); ?>

The basic structure is the same as index.php – the header is displayed, then the content, then the footer. Looking in more detail we see this:

<div id="content" class="widecolumn"> 

The front page layout uses ‘narrowcolumn’. Here we use ‘widecolumn’. Although no use of this has been made in our style so far, it does give you extra hooks to change the appearance via CSS.

We also find this:

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

Although it might seem counter-intuitive that a single page post still has The Loop, this does keep out theme consistent. WordPress ensures that have_posts will only return one post. Using The Loop here allows the theme to also be used in situations where WordPress could possibly give us more than one post.

Like the front page, there are navigational links:

<div class="navigation">
  <div class="alignleft"><?php previous_post('&laquo; %','','yes') ?></div>
  <div class="alignright"><?php next_post(' % &raquo;','','yes') ?></div>
</div>

This time the links direct us to the previous and next posts. The style is identical to the front page, with the only difference being the functions called.

Next we have the bulk of the work – the post:

<div class="post">
  <h2 id="post-<?php the_ID(); ?>"><a href="<?php echo get_permalink() ?>" rel="bookmark" title="Permanent Link: <?php the_title(); ?>"><?php the_title(); ?></a></h2>

  <div class="entrytext">
    <?php the_content('<p class="serif">Read the rest of this entry &raquo;</p>'); ?>
    <?php link_pages('<p><strong>Pages:</strong> ', '</p>', 'number'); ?>

    <p class="postmetadata alt">
      <small>
        This entry was posted
        <?php /* This is commented, because it requires a little adjusting sometimes.
          You'll need to download this plugin, and follow the instructions:
          http://binarybonsai.com/archives/2004/08/17/time-since-plugin/ */

          /* $entry_datetime = abs(strtotime($post->post_date) - (60*120)); echo time_since($entry_datetime); echo ' ago'; */ ?>
        on <?php the_time('l, F jS, Y') ?> at <?php the_time() ?>
        and is filed under <?php the_category(', ') ?>.
        You can follow any responses to this entry through the <?php comments_rss_link('RSS 2.0'); ?> feed.

        <?php if (('open' == $post-> comment_status) && ('open' == $post->ping_status)) {
          // Both Comments and Pings are open ?>
          You can <a href="#respond">leave a response</a>, or <a href="<?php trackback_url(display); ?>">trackback</a> from your own site.
       
        <?php } elseif (!('open' == $post-> comment_status) && ('open' == $post->ping_status)) {
          // Only Pings are Open ?>
          Responses are currently closed, but you can <a href="<?php trackback_url(display); ?> ">trackback</a> from your own site.

        <?php } elseif (('open' == $post-> comment_status) && !('open' == $post->ping_status)) {
          // Comments are open, Pings are not ?>
          You can skip to the end and leave a response. Pinging is currently not allowed.
 
        <?php } elseif (!('open' == $post-> comment_status) && !('open' == $post->ping_status)) {
          // Neither Comments, nor Pings are open ?>
          Both comments and pings are currently closed.     
       
        <?php } edit_post_link('Edit this entry.','',''); ?>
      </small>
    </p>
  </div>
</div>

We’ve already discussed most of the detail here, although one noticeable difference is the amount of meta information provided – a single page post displays a lot more. Several points are worth noting:

<div class="entrytext">
  <?php the_content('<p class="serif">Read the rest of this entry &raquo;</p>'); ?>

The content is again wrapped inside a div element, but this time it is called ‘entrytext’ rather than ‘entry’. Curiously the_content is called with a ‘more text’ parameter. The Codex states that the_content will ignore the ‘more text’ parameter on single posts. I assume it is included here for completeness, and in case The Loop does actually contain multiple posts.

<?php link_pages('<p><strong>Pages:</strong> ', '</p>', 'number'); ?> 

This is a new function that displays links to all the pages in the post. Multiple pages are defined by the <!--nextpage--> quicktag.

<p class="postmetadata alt">

This is the beginning of the meta data section. Like the front page, this uses the ‘postmetadata’ CSS class, but additionally it also includes the ‘alt’ class. Having multiple CSS styles is perfectly valid, and the browser will combine the styles.

Next we have a piece of commented PHP code:

<?php /* This is commented, because it requires a little adjusting sometimes.
        You'll need to download this plugin, and follow the instructions:
        http://binarybonsai.com/archives/2004/08/17/time-since-plugin/ */

/* $entry_datetime = abs(strtotime($post->post_date) - (60*120)); echo time_since($entry_datetime); echo ' ago'; */
?>

Because this appears within a PHP comment it will not be executed and will not appear in the HTML at all. It is simply a hook provided by Kubrick to allow a ‘time since’ plugin to be enabled.

Finally we have the meta-data:

<?php if (('open' == $post-> comment_status) && ('open' == $post->ping_status)) {
// Both Comments and Pings are open ?>
You can <a href="#respond">leave a response</a>, or <a href="<?php trackback_url(display); ?>">trackback</a> from your own site.

<?php } elseif (!('open' == $post-> comment_status) && ('open' == $post->ping_status)) {
// Only Pings are Open ?>
Responses are currently closed, but you can <a href="<?php trackback_url(display); ?> ">trackback</a> from your own site.

<?php } elseif (('open' == $post-> comment_status) && !('open' == $post->ping_status)) {
// Comments are open, Pings are not ?>
You can skip to the end and leave a response. Pinging is currently not allowed.

<?php } elseif (!('open' == $post-> comment_status) && !('open' == $post->ping_status)) {
// Neither Comments, nor Pings are open ?>
Both comments and pings are currently closed.     

<?php } edit_post_link('Edit this entry.','',''); ?>

Although it looks confusing it just checks various attributes of the post, and displays information accordingly.

Styling the single page

The first step in styling single.php is to introduce the wrapper element from index.php. This will immediately cause the page to display properly.

<?php get_header(); ?>

<div id="wrapper">
  <div id="content" class="widecolumn">
       
  <?php if (have_posts()) : while (have_posts()) : the_post(); ?>
    <div class="navigation">
      <div class="alignleft"><?php previous_post('&laquo; %','','yes') ?></div>
      <div class="alignright"><?php next_post(' % &raquo;','','yes') ?></div>
    </div>
 
    <div class="post">Post code</div>
   
  <?php comments_template(); ?>
 
  <?php endwhile; else: ?>
 
    <p><?php _e('Sorry, no posts matched your criteria.'); ?></p>
 
<?php endif; ?>
  </div>
	<?php get_sidebar(); ?>
</div>

<?php get_footer(); ?>

The file is now almost identical to index.php. I have also inserted the sidebar – I like to be able to navigate wherever I am. You are free to leave this out if you prefer the Kubrick style. Notice I’ve also removed the commented PHP code for the date plugin, just to tidy the file.

When we look at our page everything is almost normal:

Fixed single bug

It is now apparent that a style for the next and previous posts needs to be added. As the front page also uses the same CSS class we can style them both together.

.navigation {
  display: block;
  margin-top: 10px;
  margin-bottom: 40px;
}
 
.alignleft {
  float: left;
  text-align: left;
  width: 50%;
}

.alignright {
  float: right;
  text-align: right;
  width: 50%;
}

Note that when using the ‘float’ attribute, you should always try and define a width.

This gives us proper navigational elements:

Fixed navigation

Meta data

There are still several key elements we have not yet styled. One of these is the meta-data – information about the post itself. The CSS class assigned to the meta-data in both single.php and index.php is ‘metadata’. We’ve already defined this style for index.php, so single.php will inherit it:

Meta-data before

This isn’t quite right in this context. If we change the metadata style then we also change the style on the front page, which is not what we want. We could rename the meta-data class in single.php and create a new class, but this is not necessary. Instead we can make use of the fact that a post in a single.php is enclosed within the ‘entrytext’ div. This allows us to style the meta-data specifically for single page posts:

.entrytext .postmetadata {
  font-size: 1.1em;
  background-color: #FDE5C3;
  width: 70%;
  margin: 2em auto3.5em auto;
  border: 1px dotted #e9b17b;
  padding: 5px;
  padding-left: 45px;
  background: #FDE5C3 url(images/metadata.png) no-repeat   scroll top left;
}

So what’s going on here? The font has been made bigger, and I’ve given the whole element a border and background colour. The width has been shrunk and the element centered. The most noticeable addition has been a background image, with all text offset from the left margin. The effect of this is:

Meta-data after

Much better.

The final element

One function that needs mentioning is this:

<?php comments_template(); ?> 

Looking at the Codex we see that it simply includes comments.php from our theme. This file displays all the comments for the post, and will be discussed in detail in the next section.

105 comments

  1. Superb, Stunning and Diligent… I was just looking how to design a WordPress theme from my HTML template and this is one tutorial which was just perfect. Thanks for all the painstaking documentation. God Bless!

  2. Thank YOU!

    I thought I knew CSS but one look at the Kubrick style.css made wonder whether I really knew it. Anyway, that look was enough to put aside the theme redesign project I had in mind, particularly when I found that whatever changes I make to the stylesheet, the header had the same blue background. It was only when I went through your tutorial that I really was able to identify the problem and go to the Admin panel of wordpress.

    Your tutorial provided many more such insights into small details with big impact.

    Above all, it provided the confidence to start on the theme redesign project.

    Once again, Thank YOU!

Leave a comment

Your email address will not be published. Required fields are marked *