UPDATE: download the Childishly Simple Theme and look in functions.php for the latest version of this code. See a demo of the comments here.
Threaded comments have been a feature of WordPress ever since WordPress 2.7 was released in Dec. 2008.
This tutorial will show you how to style threaded comments, highlighting a specific user’s comments (in this case a user called Admin).
Note: this is a hack, and a slightly complicated hack at that. The rounded corners are created using images, and that’s because Internet Explorer doesn’t support rounded borders yet (sigh). Images don’t scale well – if, for some reason, a user decides to zoom out (make the screen smaller) it becomes obvious that corner images have been used. Hopefully most users won’t zoom out.
The other aspect of this hack is that it depends on the creation of a class div.comment-body and CSS to get round WordPress shortcomings. WordPress unnecessarily nests comments, creating extra divs that are a pain to work around ( the divs don’t need to be nested as they can be indented e.g. div.depth-2 can be indented 40px in from the left). If there weren’t nested divs there wouldn’t be the need to add in div.comment-body and it would simplify the CSS immeasurably.
WordPress could also make things easier if it added in some extra classes on the main div surrounding each comment. These classes would add the depth information.
Currently the classes around each comment look something like this:
comment byuser comment-author-admin bypostauthor odd alt depth-1
Life would (in my lazy opinion) be a lot simpler if they looked like this:
comment comment-depth-1 byuser byuser-depth-1 comment-author-admin comment-author-admin-depth-1 bypostauthor bypostauthor-depth-1 odd odd-depth-1 alt alt-depth 1 depth-1
Clearly no-one in their right might would use all the classes at once, but imagine that one day Internet Explorer gets its act together and rounded-corner hacks aren’t necessary. Styling comments would then be as simple as choosing the following – not necessarily elegant, but foolproof.
.comment .comment-depth-1 .comment-depth-2 .comment-depth-3 .comment-depth-4 .comment-depth-5 .comment-author-admin .comment-author-admin-depth-1 .comment-author-admin-depth-2 .comment-author-admin-depth-3 .comment-author-admin-depth-4 .comment-author-admin-depth-5
Anyhow, let’s first take a look at a screenshot to see what we’re aiming for.

There’s quite a lot of things to note:
- Although you can’t see it, comments are nested one within another. WordPress automatically uses nested divs, which are unnecessary and which complicate matters – but there’s no easy way round that yet.
- The admin or any other specified user can have their comments styled uniquely, still using background images.
- Comments are numbered and trackbacks are numbered.
- Trackbacks are separated from comments.
Let’s have a look at the sort of HTML we’re aiming for:
<div class="comment even thread-even depth-1" id="comment-17">
<div class="comment-body">
<span class="comment-top-left"> </span>
<span class="comment-top-right"> </span>
<span class="comment-bottom-left"> </span>
<span class="comment-bottom-right"> </span>
<p class="comment-meta">
<span class="comment-number">3 </span>
<span class="comment-author">By Dave</span>
<span class="comment-date">
Wed Feb 10th 2010 at 7:16 pm</span>
<span class="comment-edit"> </span>
</p>
<img alt='' src='http://0.gravatar.com/avatar/207bb95317c3cab17f3461c3a38cdb74?s=32&=G'
class='avatar avatar-32 photo' height='32' width='32' />
<p>Third comment</p>
<p class="comment-reply-link"><a rel='nofollow' class='comment-reply-link' href='/Elephants
/?replytocom=17#respond' onclick=
'return addComment.moveForm("comment-17", "17", "respond", "110")'>Reply to this person</a></p>
</div><!--Close comment-body-->
</div>
How it’s going to work
It’s pretty simple. There’ll be an absolutely positioned span in each corner, and each span will have a background image of a rounded corner (it would be nice if Internet Explorer supported rounded borders, but it doesn’t.) There’s an avatar image, absolutely positioned in relation to the comment, and there’s a paragraph element with a rollover effect (using CSS) that contains a ‘reply to this person’ link.
Now we need the php and css that makes it work.
Download
Rather than copy and paste the various bits of code and CSS below (you can never tell how accurately code snippets pasted into a PHP page will work) here’s a zip file you can download that contains all the images and the code used in this tutorial.
Comments.php
First we’re going to mess around with comments.php
Somewhere in comments.php you might have something like this:
<?php if ( have_comments() ) : // If there are comments ? >
<h3 id="comments" > <?php comments_number('No Responses', 'One Response', '% Responses' );? > to
"<?php the_title(); ? > "</h3 >
<ol class=" commentlist" >
<?php wp_list_comments(); ? >
</ol >
<div class=" navigation" >
<div class=" alignleft" > <?php previous_comments_link() ? > </div >
<div class=" alignright" > <?php next_comments_link() ? > </div >
</div >
<?php else : // if there are no comments or pingbacks then do whatever follows ?>
For the sake of simplicity delete it or comment it out and replace it with this:
<?/*
With thanks to
http://ottodestruct.com/blog/2008/wordpress-27-comments-enhancements/#more-424
and http://sivel.net/2008/10/wp-27-comment-separation/
*/?>
<?php if ( have_comments() ) : // If there are comments or pingbacks then do the stuff below ?>
<?php $countcomments=0; // See functions.php for this variable, which outputs the number of comments ?>
<?php if ( ! empty($comments_by_type['comment']) ) :
// If there are comments as distinct from pingbacks, do the following?>
<h3 id='comments'><?php comments_number('No Responses', 'One Response', '% Responses' );?> to
'<?php the_title(); ?>'</h3>
<div class='commentlist'>
<?// Contains all the comments and comment navigation but not the comment form. ?>
<?php wp_list_comments('type=comment&callback=list_comments&style=div'); ?>
<?/*
<?php wp_list_comments('type=comment&callback=list_comments&style=div'); ?> refers to a function in
functions.php called list_comments You can change the layout of your comments there.
See also http://codex.wordpress.org/Template_Tags/wp_list_comments
*/?>
<?php endif;?>
<?// End if there are comments as distinct from pingbacks ?>
<div class='comment-navigation'>
<p class='comment-navigation comment-floatright'><?php next_comments_link('Newer Comments') ?></p>
<p class='comment-navigation comment-floatleft'><?php previous_comments_link('Older Comments') ?></p>
</div><!–Close comment-navigation–>
<?/*
The navigation links above display Older Comments and Newer Comments when you have paged comments.
Page urls will look something like this: http://www.mysite.com/bananas/comment-page-1/#comments
The pages will display oldest or newest comments first (ordered by top-level comments).
The number of comments shown is set in Admin Panel / Settings / Discussion and is counted by top-level comments
(i.e. the first comment in each mini-thread).
To show newer comments first (most recent comments) in Admin Panel / Settings / Discussion set
last page displayed by default and newer comments at the top of each page.
To show older comments first in Admin Panel / Settings / Discussion set first page displayed
by default and older comments at the top of each page.
*/?>
<?php $countpings=0; // See functions.php for this variable, which outputs the number of pings ?>
<?php if ( ! empty($comments_by_type['pings']) ) : // If there are pingbacks / trackbacks ?>
<h3 class='pings'>Pings and Trackbacks</h3>
<?php wp_list_comments('type=pings&callback=list_pings&style=div');
/*Output the pingbacks using list_pings in functions.php*/?>
<?php endif; ?>
<?/*
The pings / trackbacks have to come after the paged navigation links otherwise the paged navigation
links won't work. If you don't want trackbacks to show then simply delete the above code.
*/?>
</div><!--Close commentlist-->
<?php else : // if there are no comments or pingbacks then do whatever follows ?>
The above code says that if there are comments then fetch the function list_comments from functions.php and if there are pings then fetch the function list_pings from functions.php
Each function can be tweaked within functions.php to output the HTML that you desire.
Functions.php
Let’s head over to functions.php
Functions.php should exist in your theme, and if not you can create a file of that name. Most importantly, the very first character in functions.php should be <?php and the very last character should be ?> otherwise you’ll get error messages about headers being already sent.
In functions.php we’re going to paste in this little lot (note that spaces and non-breaking spaces aren’t showing – that’s why you need to download the zip file. I should use a code plugin but they all look so complicated
)
/* WITH THANKS TO:
http://codex.wordpress.org/Template_Tags/wp_list_comments
http://codex.wordpress.org/Glossary trackbacks and pingbacks
http://sivel.net/2008/10/wp-27-comment-separation/
WP nests comments unnecessarily. They only need a unique class and then they can be indented.
*/
function list_comments($comment, $args, $depth)
{
$GLOBALS['comment'] = $comment;
?>
<?php global $countcomments; // The variable $countcomments can be used outside this function ?>
<div <?php comment_class(); ?> id="comment-<?php comment_ID() ?>"> <?/* Add an ID to the div*/?>
<div class="comment-body">
<span class="comment-top-left"> </span>
<span class="comment-top-right"> </span>
<span class="comment-bottom-left"> </span>
<span class="comment-bottom-right"> </span>
<?/*
The above images can be deleted when Internet Explorer gets its act together and supports rounded corners.
div.comment-body can then have the appropriate CSS given to it in style.css
*/?>
<p class="comment-meta">
<?/* An optional comment number*/?>
<?php $countcomments ++; ?>
<span class="comment-number"><?php echo $countcomments; ?> </span>
<?php printf(__('<span class="comment-author">By %s</span> '), get_comment_author_link())
/*
The commenter's name and link to their website. If you'd like to display only their name but still have their
website url in case you want to give them a link in your post, then use this:
<p class="comment-author">By <?php comment_author(); ?></p>
*/?>
<span class="comment-date">
<?php the_time ('D M jS Y') ?> at <?php the_time ('g:i a') // Comment date and time -
more info. at: http://uk2.php.net/date ?>
</span>
<span class="comment-edit">
<?php edit_comment_link(__('(Edit Comment)'),' ','') // Link to edit the comment (if logged in)?>
</span>
</p> <?// End comment-meta?>
<?php echo get_avatar( $comment, 32 ); // Get the Avatar image - get the comment object,
set the size to 32px by 32 px ?>
<?php if ($comment->comment_approved == '0') : ?><p class="moderation"><?php _e('Your comment is awaiting
moderation. Although you can see this, no one else can.') ?></p><br /><?php endif; // If awaiting moderation ?>
<?php comment_text() // The text of the comment ?>
<p class="comment-reply-link"><?php comment_reply_link(array_merge( $args, array('depth' => $depth,
'reply_text' => '
Reply to this person', 'login_text' => 'Log in to reply to this', 'max_depth' => $args['max_depth']))) ?></p>
</div><!--Close comment-body-->
<?php
}
// LAST DIV IS ADDED AUTOMATICALLY BY WORDPRESS.
//END CUSTOM COMMENTS
/* START CUSTOM PINGS - with thanks to http://sivel.net/2008/10/wp-27-comment-separation/
Uses the same HTML as the comments except for the ping count.*/
function list_pings($comment, $args, $depth)
{
$GLOBALS['comment'] = $comment;
?>
<?php global $countpings; // The variable $countpings can be used outside this function ?>
<div class="comment depth-1">
<div class="comment-body">
<span class="comment-top-left"> </span>
<span class="comment-top-right"> </span>
<span class="comment-bottom-left"> </span>
<span class="comment-bottom-right"> </span>
<p class="comment-meta">
<?/* An optional comment number*/?>
<?php $countpings ++; // Add one to the variable $countpings ?>
<span class="ping-number"><?php echo $countpings; // Output the ping number?></span>
<?php printf(__('<span class="comment-author">By %s</span> '), get_comment_author_link()) ?>
<span class="comment-date">
<?php the_time ('D M jS Y') ?> at <?php the_time ('g:i a') // Comment date and time -
more info. at: http://uk2.php.net/date ?>
</span>
<span class="comment-edit">
<?php edit_comment_link(__('(Edit Comment)'),' ','') // Link to edit the comment (if logged in)?>
</span>
</p> <?// End comment-meta?>
<?php comment_text() // The ping text ?>
</div><!--Close comment-body-->
<?php }
// LAST DIV IS ADDED AUTOMATICALLY BY WORDPRESS.
//END CUSTOM PINGS
The CSS
Now for the CSS.
Paste the following into the bottom of your stylesheet style.css (by pasting it into the bottom of the stylesheet you ensure that it overrides any existing styles with the same name).
/*
Text saying there are X responses to (your post name)
*/
h3#comments {margin:20px 0 20px 0;}
/*
This div contains all the comments but not the comment navigation or comment form
*/
div.commentlist {position:relative;margin:0 0 0 20px;}
/*
This div contains everything in each individual comment
*/
div.comment{position:relative;padding:0;}
/*
The comment text within the comment body
*/
div.comment p {margin:0 10px 0 20px;}
/*
The different depths of comments
*/
div.depth-1 {position:relative;margin:0 0 20px 0;background-color:transparent;padding:0;width:640px;}
div.depth-2 {position:relative;margin:20px 0 0 20px;background-color:transparent;padding:0;width:620px;}
div.depth-3 {position:relative;margin:20px 0 0 20px;background-color:transparent;padding:0;width:600px;}
div.depth-4 {position:relative;margin:20px 0 0 20px;background-color:transparent;padding:0;width:580px;}
div.depth-5 {position:relative;margin:20px 0 0 20px;background-color:transparent;padding:0;width:560px;}
/*
Contains everything to do with each individual comment, and creates the border. The border is given a radius but Internet Explorer doesn’t support this. When it finally gets round to doing so you can delete the images comment-top-left.gif, comment-top-right.gif etc. from functions.php
*/
div.comment div.comment-body {position:relative;margin:20px 0 20px 0;padding:5px 0 5px 0; background-color:transparent;border:1px solid #BBBBBB;-moz-border-radius: 10px;-webkit-border-radius: 10px;}
/*
The corner images on an ordinary comment. Note that div.comment-body above has rounded corners for browsers other than Internet Explorer. When Internet Explorer supports rounded corners this rounded corner hack will no longer be necessary.
*/
div.comment div.comment-body span.comment-top-left {position:absolute; top:-1px;left:-1px;width:18px;height:18px;background:transparent url(images/comment-top-left.png) top left no-repeat}
div.comment div.comment-body span.comment-top-right {position:absolute; top:-1px;right:-1px;width:18px;height:18px;background:transparent url(images/comment-top-right.png) top right no-repeat}
div.comment div.comment-body span.comment-bottom-left {position:absolute; bottom:-1px;left:-1px;width:18px;height:18px;background:transparent url(images/comment-bottom-left.png) top left no-repeat}
div.comment div.comment-body span.comment-bottom-right {position:absolute;bottom:-1px;right:-1px;width:18px;height:18px;background:transparent url(images/comment-bottom-right.png) top right no-repeat}
/*
The Avatar on a user comment – see http://codex.wordpress.org/Using_Gravatars
Make sure specific styles for specific users come afte this as later styles override previous styles.
*/
div.comment img.avatar{position:absolute;left:-43px;top:15px;padding:0 13px 0 0;background:transparent url(images/avatar-arrow.png) top left no-repeat;z-index:100;}
/*
The admin comment body
*/
div.comment-author-admin div.comment-body {position:relative;margin:20px 0 20px 0;padding:5px 0 5px 0; background-color:#E6FFED;border:1px solid #666666;-moz-border-radius: 10px;-webkit-border-radius: 10px;}
/*
The corner images on an admin comment. Note that div.comment-body above has rounded corners for browsers other than Internet Explorer. When Internet Explorer supports rounded corners this rounded corner hack will no longer be necessary.
*/
div.comment-author-admin div.comment-body span.comment-top-left {position:absolute; top:-1px;left:-1px;width:18px;height:18px;background:transparent url(images/comment-top-left-admin.png) top left no-repeat;}
div.comment-author-admin div.comment-body span.comment-top-right {position:absolute; top:-1px;right:-1px;width:18px;height:18px;background:transparent url(images/comment-top-right-admin.png) top right no-repeat}
div.comment-author-admin div.comment-body span.comment-bottom-left {position:absolute; bottom:-1px;left:-1px;width:18px;height:18px;background:transparent url(images/comment-bottom-left-admin.png) top left no-repeat}
div.comment-author-admin div.comment-body span.comment-bottom-right {position:absolute;bottom:-1px;right:-1px;width:18px;height:18px;background:transparent url(images/comment-bottom-right-admin.png) top right no-repeat}
/*
The avatar for the user with a user name of Admin
Make sure this style comes after div.comment-body > img.avatar as later styles override previous styles.
*/
div.comment-author-admin img.avatar {position:absolute;left:-43px;top:15px;padding:0 13px 0 0;background:transparent url(images/avatar-arrow-admin.png) top left no-repeat;}
/*
Date, author, edit
*/
p.comment-meta {position:relative;font-size:1.0em;font-weight:normal;font-style:normal;}
/*
The comment number
*/
p.comment-meta span.comment-number {position:absolute;display:block; left:-36px;top:50px;color:#C0C0C0; height:1.2em;margin:0;padding:0;font-size:0.8em;}
/*
The ping number
*/
p.comment-meta span.ping-number {position:absolute;display:block; left:-36px;top:10px;color:#C0C0C0; height:1.2em;margin:0;padding:0;font-size:0.8em;}
/*
The name of the person who left the comment
*/
p.comment-meta span.comment-author {position:relative;font-style:normal;padding:0 0 0 20px;margin:0;color:gray;background:transparent url(images/comments-small.png) 0px 0px no-repeat;}
/*If there’s a link to their website their name is linked to their website*/
a.url:link {color:black;text-decoration:none;}
a.url:visited {color:black;text-decoration:none;}
a.url:hover {color:red;text-decoration:none;}
/*
The date the comment was left
*/
p.comment-meta span.comment-date {position:relative;font-style:normal;margin:0;padding:0 0 0 60px;color:gray;background:transparent url(images/date-small.png) 40px 0px no-repeat;}
p.comment-meta span.comment-date a:link {color:gray;text-decoration:none;}
p.comment-meta span.comment-date a:visited {color:gray;text-decoration:none;}
p.comment-meta span.comment-date a:hover {color:red;text-decoration:none;}
/*
Your comment is awaiting moderation
*/
p.moderation {color:red;}
/*
For the administrator to edit comments and edit pings / trackbacks
*/
p.comment-meta span.comment-edit {position:relative;margin:0 0 0 10px;font-size:0.9em;}
a.comment-edit-link:link {color:red;text-decoration:none;}
a.comment-edit-link:visited {color:red;text-decoration:none;}
a.comment-edit-link:hover {color:red;text-decoration:none;}
/*
Reply to a specific comment by clicking the link contained within this paragraph element
*/
p.comment-reply-link {position:relative;text-align:left;font-weight:normal;font-size:12px; width:300px;text-indent:0;padding:5px 0 5px 0;}
/*
Reply to a specific comment by clicking this link
*/
a.comment-reply-link:link {color:#6A6A6A;text-decoration:none;background:transparent url(images/reply.png) 0px 0px no-repeat;width:200px;text-indent:0;margin:0;padding:3px 20px 5px 8px;}
a.comment-reply-link:visited {color:#6A6A6A;text-decoration:none;background:transparent url(images/reply.png) 0px 0px no-repeat;width:200px;text-indent:0;margin:0;padding:3px 20px 5px 8px;}
a.comment-reply-link:hover {color:white;text-decoration:none;background:transparent url(images/reply-rollover.png) 0px 0px no-repeat;width:200px;text-indent:0;margin:0;padding:3px 20px 5px 8px;}
/*
The reply link if users need to be logged in
*/
p.comment-reply-link a.comment-reply-login:link {color:red;font-weight:normal;text-decoration:none;background:transparent url(images/reply.png) 0px 0px no-repeat;width:200px;text-indent:0;margin:0;padding:3px 20px 5px 8px;}
p.comment-reply-link a.comment-reply-login:visited {color:red;font-weight:normal;text-decoration:none;background:transparent url(images/reply.png) 0px 0px no-repeat;width:200px;text-indent:0;margin:0;padding:3px 20px 5px 8px;}
p.comment-reply-link a.comment-reply-login:hover {color:#FF7D7D;font-weight:normal;text-decoration:none;background:transparent url(images/reply-rollover.png) 0px 0px no-repeat;width:200px;text-indent:0;margin:0;padding:3px 20px 5px 8px;}
/*
The header above the pings
*/
h3.pings {color:red;margin:20px 0 20px 0;}
/*
Comment navigation container
*/
div.comment-navigation {width:100%;margin:30px 0 30px 0;padding:0;overflow:auto;border:0;background:transparent;border:0;}
/*
Previous and Next page comment navigation. For some reason a border prevents a scrollbar appearing in FireFox when previous/next links are clicked
*/
p.comment-navigation {position:relative;}
p.comment-floatleft {float:left;text-align:left;border:1px solid white;text-indent:0;padding:0;margin:0;} /*Border prevents scrollbars*/
p.comment-floatright {float:right;text-align:right;border:1px solid white;text-indent:0;padding:0;margin:0;} /*Border prevents scrollbars*/
p.comment-navigation a:link {color:red;text-decoration:none;border:0;text-indent:0;padding:0;margin:0;}
p.comment-navigation a:visited {color:red;text-decoration:none;border:0;text-indent:0;padding:0;margin:0;}
p.comment-navigation a:hover {color:gray;text-decoration:none;border:0;text-indent:0;padding:0;margin:0;}
Images
All that remains is to upload the images to a file within your theme called images.
Download
Here’s a zip file you can download that contains all the images and the code used in the tutorial above.
By ffff Wed Jul 7th 2010 at 9:20 pm
Testing
By admin Wed Jul 7th 2010 at 9:22 pm
More testing
By admin Wed Jul 7th 2010 at 9:22 pm
See, it works!
By masjigen Wed Oct 27th 2010 at 5:53 pm
Testing threaded comment
By Anonymous Sun Jul 24th 2011 at 12:06 pm
Very good tutorial. I especially like the speech bubble style.