Creating Your First Custom Standard WordPress Comment Templates Without Breaking Site Responsiveness
Understanding WordPress Comment Template Hierarchy
Before diving into customization, it’s crucial to grasp how WordPress resolves comment templates. When a theme needs to display comments, it follows a specific hierarchy. The primary template file for comments is comments.php. However, WordPress also looks for more specific templates based on the post type. For instance, if you’re on a custom post type called ‘product’, WordPress will first look for product-comments.php. If that’s not found, it falls back to comments.php. This hierarchy is key to creating targeted comment displays without affecting other parts of your site.
Creating a Basic Custom Comment Template
Let’s start with a straightforward modification. We’ll create a new comments.php file in our theme’s root directory. If your theme already has one, we’ll be modifying it. If not, we’ll create it from scratch. This file will contain the HTML structure and PHP logic to display comments and the comment form.
Here’s a minimal comments.php structure:
<?php
/**
* The template for displaying comments.
*
* This file is used to display the comments.
*
* @package YourThemeName
*/
if ( post_password_required() ) {
return;
}
?>
<!-- wp:paragraph --><div id="comments" class="comments-area">
<?php if ( have_comments() ) : ?>
<h2 class="comments-title">
<?php
$comment_count = get_comments_number();
if ( 1 === $comment_count ) {
esc_html_e( 'One thought on “%1$s”', 'yourthemename' );
} else {
printf( esc_html__( '%1$s thoughts on “%2$s”', 'yourthemename' ), number_format_i18n( $comment_count ), get_the_title() );
}
?>
</h2>
<ol class="comment-list">
<?php
wp_list_comments( array(
'style' => 'ol',
'short_ping' => true,
'avatar_size' => 60,
) );
?>
</ol><!-- .comment-list -->
<?php if ( get_comment_pages_count() > 1 && get_option( 'page_comments' ) ) : ?>
<nav id="comment-nav-below" class="comment-navigation">
<h1 class="screen-reader-text"><?php esc_html_e( 'Comment navigation', 'yourthemename' ); ?></h1>
<div class="nav-previous"><?php previous_comments_link( esc_html__( '⇦ Older Comments', 'yourthemename' ) ); ?></div>
<div class="nav-next"><?php next_comments_link( esc_html__( 'Newer Comments ⇨', 'yourthemename' ) ); ?></div>
</nav><!-- #comment-nav-below -->
<?php endif; ?>
<?php endif; ?>
<?php
// If comments are closed and there is no comment status, leave the comments template.
if ( ! comments_open() && get_comments_number() && post_type_supports( get_post_type(), 'comments' ) ) :
?>
<p class="no-comments"><?php esc_html_e( 'Comments are closed.', 'yourthemename' ); ?></p>
<?php endif; ?>
<?php
comment_form( array(
'comment_field' => '<div><label for="comment">' . esc_html__( 'Comment', 'yourthemename' ) . '</label><textarea id="comment" name="comment" cols="45" rows="8" aria-required="true"></textarea></div>',
'class_submit' => 'submit',
'label_submit' => esc_html__( 'Post Comment', 'yourthemename' ),
) );
?>
</div><!-- #comments -->
In this example:
- We check if the post is password protected.
- We use
have_comments()to see if there are any comments to display. get_comments_number()andget_the_title()are used to dynamically generate the comment count heading.wp_list_comments()is the core function that iterates through and displays each comment. We’ve passed arguments for styling, short ping, and avatar size.- Comment pagination is handled with
get_comment_pages_count(),previous_comments_link(), andnext_comments_link(). - The comment form is displayed using
comment_form(). We’ve customized the ‘comment_field’ and ‘label_submit’ arguments. - All output is escaped using WordPress’s sanitization functions like
esc_html_e()andesc_html__()for security.
Styling Your Custom Comments
The PHP file only structures the HTML. To make it look good and ensure responsiveness, you’ll need to add CSS. This CSS should be placed in your theme’s style.css file or a dedicated CSS file enqueued by your theme.
Here’s some example CSS to get you started:
.comments-area {
margin-top: 30px;
padding: 20px;
background-color: #f9f9f9;
border: 1px solid #eee;
border-radius: 5px;
}
.comments-title {
font-size: 1.8em;
margin-bottom: 20px;
border-bottom: 1px solid #ddd;
padding-bottom: 10px;
}
.comment-list {
list-style: none;
padding: 0;
margin: 0;
}
.comment-list .comment {
margin-bottom: 25px;
padding-bottom: 25px;
border-bottom: 1px dotted #ccc;
display: flex; /* For avatar alignment */
align-items: flex-start; /* Align items to the top */
}
.comment-list .comment:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.comment-author .avatar {
margin-right: 15px;
border-radius: 50%;
width: 60px; /* Matches avatar_size in wp_list_comments */
height: 60px;
flex-shrink: 0; /* Prevent avatar from shrinking */
}
.comment-body {
flex-grow: 1; /* Allow comment body to take remaining space */
}
.comment-meta {
font-size: 0.9em;
color: #777;
margin-bottom: 10px;
}
.comment-author .fn {
font-weight: bold;
color: #333;
}
.comment-content p {
margin-bottom: 15px;
line-height: 1.6;
}
.comment-reply-link {
display: inline-block;
margin-top: 10px;
padding: 5px 10px;
background-color: #eee;
border-radius: 3px;
text-decoration: none;
color: #555;
}
.comment-reply-link:hover {
background-color: #ddd;
}
/* Comment Form Styling */
.comment-form {
margin-top: 30px;
padding-top: 30px;
border-top: 1px solid #eee;
}
.comment-form label {
display: block;
margin-bottom: 10px;
font-weight: bold;
}
.comment-form input[type="text"],
.comment-form input[type="email"],
.comment-form input[type="url"],
.comment-form textarea {
width: 100%;
padding: 10px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box; /* Include padding and border in the element's total width and height */
}
.comment-form textarea {
min-height: 120px;
resize: vertical;
}
.comment-form .submit {
background-color: #0073aa;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1em;
transition: background-color 0.3s ease;
}
.comment-form .submit:hover {
background-color: #005177;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.comments-area {
padding: 15px;
}
.comment-list .comment {
flex-direction: column; /* Stack avatar and comment body on smaller screens */
align-items: center; /* Center items when stacked */
text-align: center;
}
.comment-author .avatar {
margin-right: 0;
margin-bottom: 15px;
}
.comment-body {
text-align: left; /* Align comment text left when stacked */
}
.comment-form input[type="text"],
.comment-form input[type="email"],
.comment-form input[type="url"],
.comment-form textarea {
width: calc(100% - 20px); /* Adjust for padding */
}
}
Advanced: Customizing Comment Structure with `wp_list_comments` Callbacks
For more granular control over how each individual comment is displayed, you can use the callback argument of the wp_list_comments() function. This allows you to define a custom PHP function that will be responsible for rendering each comment’s HTML.
First, define your callback function. This function will receive an object containing comment data as its argument. You can place this function in your theme’s functions.php file.
<?php
/**
* Custom callback for displaying comments.
*
* @param object $comment The comment object.
* @param array $args An array of comment arguments.
* @param int $depth The depth of the comment.
*/
function my_custom_comment_callback( $comment, $args, $depth ) {
$GLOBALS['comment'] = $comment; // Ensure the global $comment object is available
?>
<li id="comment-<?php comment_ID(); ?>" <?php comment_class( empty( $args['toplevel_comment'] ) ? 'comment' : 'comment depth-' . $depth ); ?>>
<article id="div-comment-<?php comment_ID(); ?>" class="comment-body">
<footer class="comment-meta">
<div class="comment-author vcard">
<?php if ( 0 != $comment->user_id ) echo get_avatar( $comment, $args['avatar_size'] ); ?>
<?php printf( '<b class="fn">%s</b>', get_comment_author_link() ); ?>
<span class="says"><?php esc_html_e( 'says', 'yourthemename' ); ?></span>
</div><!-- .comment-author -->
<div class="comment-metadata">
<a href="<?php echo esc_url( get_comment_link( $comment->comment_ID ) ); ?>">
<time datetime="<?php comment_time( 'c' ); ?>">
<?php printf( esc_html__( '%1$s at %2$s', 'yourthemename' ), get_comment_date(), get_comment_time() ); ?>
</time>
</a>
<?php edit_comment_link( esc_html__( 'Edit', 'yourthemename' ), '<span class="edit-link">', '</span>' ); ?>
</div><!-- .comment-metadata -->
</footer><!-- .comment-meta -->
<div class="comment-content">
<?php comment_text(); ?>
</div><!-- .comment-content -->
<?php if ( '0' == $comment->comment_approved ) : ?>
<p class="comment-awaiting-moderation"><?php esc_html_e( 'Your comment is awaiting moderation.', 'yourthemename' ); ?></p>
<?php endif; ?>
<div class="reply">
<?php
comment_reply_link( array_merge( $args, array(
'add_below' => 'div-comment',
'depth' => $depth,
'max_depth' => $args['max_depth'],
) ) );
?>
</div><!-- .reply -->
</article><!-- .comment-body -->
<?php // The closing tag is handled by wp_list_comments() when using the callback.
}
?>
Then, in your comments.php file, you’ll pass this function to wp_list_comments():
<?php
// ... inside your comments.php file ...
<?php if ( have_comments() ) : ?>
<h2 class="comments-title">...</h2>
<ol class="comment-list">
<?php
wp_list_comments( array(
'style' => 'ol',
'callback' => 'my_custom_comment_callback', // Use your custom callback function
'avatar_size' => 60,
'max_depth' => 5, // Set a maximum depth for nested comments
) );
?>
</ol><!-- .comment-list -->
// ... pagination and comment form ...
<?php endif; ?>
This callback approach gives you complete control over the HTML structure for each comment, including nested replies. You can add custom classes, attributes, or even conditionally display elements based on comment data.
Advanced Diagnostics: Troubleshooting Common Issues
When implementing custom comment templates, several issues can arise. Here’s how to diagnose and fix them:
Issue: Comments Not Displaying
- Check
comments.phpexistence: Ensurecomments.phpis in your theme’s root directory. If using a child theme, it must be in the child theme’s directory. - Verify
have_comments(): Add a temporary<?php echo '<p>Debug: Have comments? ' . (have_comments() ? 'Yes' : 'No') . '</p>'; ?>before the comment loop in yourcomments.php. If it outputs ‘No’, the issue might be with the post itself (e.g., comments are disabled for that post type or globally). - Check
wp_list_comments()arguments: Ensure the arguments passed towp_list_comments()are valid. Incorrect arguments can cause the function to fail silently. - Theme conflicts: Temporarily switch to a default WordPress theme (like Twenty Twenty-Three) to rule out conflicts with other plugins or theme features. If comments display correctly on the default theme, the problem lies within your custom theme.
Issue: Comment Form Not Appearing
- Check
comments_open(): Ensure comments are enabled for the post and globally in WordPress settings (Settings > Discussion). Add a debug line:<?php echo '<p>Debug: Comments open? ' . (comments_open() ? 'Yes' : 'No') . '</p>'; ?>. - Verify
comment_form()call: Make surecomment_form()is called correctly within yourcomments.phpfile. - Plugin conflicts: Some security or form plugins might interfere with the default comment form. Test by deactivating other plugins.
- Theme structure: Ensure the
comment_form()call is not wrapped in a conditional that prevents it from being displayed (e.g., inside anif (!is_user_logged_in())block without an `else` for logged-in users).
Issue: Responsiveness Problems (Layout Breaks on Mobile)
- Inspect CSS: Use your browser’s developer tools (Inspect Element) to examine the HTML structure and applied CSS. Look for elements that are not behaving as expected on smaller viewports.
- Check
box-sizing: Ensure your CSS usesbox-sizing: border-box;for form elements and containers. This is crucial for predictable sizing with padding and borders. - Flexbox/Grid issues: If you’re using Flexbox or CSS Grid for layout (e.g., aligning avatars), ensure you have appropriate media queries to adjust the layout for smaller screens. The example CSS provided uses Flexbox with a media query to stack elements vertically on smaller screens.
- Fixed-width elements: Avoid using fixed pixel widths for elements that should be fluid. Use percentages, `vw` units, or `max-width` instead.
- Image/Avatar sizes: Ensure avatars and other images are responsive. The example CSS sets a fixed avatar size but uses `flex-shrink: 0` to prevent it from distorting and `max-width: 100%` on images within the comment content.
Issue: Nested Comments Display Incorrectly
- Check
$depthin callback: If using a custom callback, ensure the$depthvariable is correctly passed and used to apply appropriate CSS classes (e.g.,depth-X) for indentation. - CSS for nesting: Your CSS needs to account for nested comments. Typically, this involves adding left padding to elements with higher depths. For example:
.comment-list .depth-2 { margin-left: 20px; } .comment-list .depth-3 { margin-left: 40px; }. max_depthargument: Ensure themax_depthargument inwp_list_comments()is set appropriately if you want to limit the nesting level.
Conclusion
By understanding the WordPress comment template hierarchy and leveraging functions like wp_list_comments() with custom callbacks, you can create highly customized and responsive comment sections for your WordPress themes. Remember to always prioritize security by escaping output and to thoroughly test your customizations across different devices and browsers.