How to Build Standard WordPress Comment Templates for Premium Gutenberg-First Themes
Understanding WordPress Comment Template Hierarchy
When WordPress renders comments on a post or page, it follows a specific template hierarchy. For standard comment display, the primary file is comments.php. However, if this file is not present, WordPress falls back to index.php or other parent theme files. For Gutenberg-first themes, which often rely heavily on block-based rendering, explicitly defining a comments.php file is crucial for consistent comment display, especially when users might disable Gutenberg for comment sections or when using custom comment layouts.
Creating a Basic comments.php File
A minimal comments.php file needs to check if there are any comments to display and then loop through them. It also needs to include the comment form. Here’s a foundational structure:
// Prevent direct access if ( ! post_password_required() ) { $comments_args = array( 'title_reply' => 'Leave a Reply', 'title_reply_to' => 'Reply to %s', 'cancel_reply_link' => 'Cancel reply', 'label_submit' => 'Post Comment', ); comment_form( $comments_args ); } if ( have_comments() ) { // Check if there are comments // Comment list the_title( '<h2 class="comments-title">', '</h2>' ); wp_list_comments( 'style' => 'ol', 'short_ping' => true ); // Pagination if needed if ( get_comment_pages_count() > 1 && get_option( 'page_comments' ) ) { the_comments_pagination( 'prev_text' => '← Previous', 'next_text' => 'Next →' ); } } // Display comments closed notice if applicable if ( ! comments_open() && get_comments_number() && ! // Check if comments are closed but there are existing comments // This is commented out because we usually want to show the form even if closed, to allow admin comments. // echo '<p class="no-comments">' . __( 'Comments are closed.' ) . '</p>'; }
Customizing the Comment Form
The comment_form() function is highly customizable. You can modify labels, add custom fields, and control the form’s structure. For a Gutenberg-first theme, you might want to ensure the form aligns with your block editor’s styling. You can pass an array of arguments to comment_form() to achieve this.
Here’s an example of how to add a custom field (e.g., a website URL, though WordPress has a default one) and modify the submit button’s class:
$comments_args = array( 'title_reply' => 'Join the Conversation', 'title_reply_before' => '<h3 id="reply-title" class="comment-reply-title">', 'title_reply_after' => '</h3>', 'cancel_reply_link' => 'Cancel Reply', 'label_submit' => 'Send My Comment', 'submit_button' => '<input name="submit" type="submit" id="submit" class="submit button button-primary" value="%1$s" />', 'comment_field' => '<p class="comment-form-comment"><textarea id="comment" name="comment" cols="45" rows="8" aria-required="true"></textarea></p>', 'fields' => 'author' => '<p class="comment-form-author"><input id="author" name="author" type="text" value="' . esc_attr( get_comment_author_link() ) . '" size="30"' . ( current_user_can( 'comment_author_email' ) ? '' : 'required="required"' ) . '></p>', 'email' => '<p class="comment-form-email"><input id="email" name="email" type="email" value="' . esc_attr( get_comment_author_email() ) . '" size="30"' . ( current_user_can( 'comment_author_email' ) ? '' : 'required="required"' ) . '></p>', 'url' => '<p class="comment-form-url"><input id="url" name="url" type="url" value="' . esc_attr( get_comment_author_url() ) . '" size="30" /></p>', ); comment_form( $comments_args );
Styling Comments with CSS
The output of wp_list_comments() and comment_form() generates HTML that you can style using CSS. For a Gutenberg-first theme, you’ll want to ensure these styles are consistent with your block editor’s visual language. Enqueue a dedicated stylesheet for your theme’s comments in your functions.php file.
// In functions.php function my_theme_enqueue_comment_styles() { if ( is_singular() && ( comments_open() || get_comments_number() ) ) { wp_enqueue_style( 'my-theme-comments', get_template_directory_uri() . '/css/comments.css', array(), '1.0' ); } } add_action( 'wp_enqueue_scripts', 'my_theme_enqueue_comment_styles' );
Then, in your css/comments.css file, you can target elements like:
.comments-title {
font-size: 1.8em;
margin-bottom: 1em;
border-bottom: 1px solid #eee;
padding-bottom: 0.5em;
}
.comment-list {
list-style: none;
padding-left: 0;
margin-bottom: 2em;
}
.comment-list li {
margin-bottom: 1.5em;
padding-bottom: 1.5em;
border-bottom: 1px solid #eee;
}
.comment-list .avatar {
float: left;
margin-right: 15px;
border-radius: 50%;
}
.comment-author {
font-weight: bold;
margin-bottom: 0.5em;
}
.comment-meta {
font-size: 0.9em;
color: #777;
margin-bottom: 0.5em;
}
.comment-content {
clear: both;
line-height: 1.6;
}
.comment-reply-link {
display: inline-block;
margin-top: 0.5em;
padding: 5px 10px;
background-color: #f0f0f0;
border-radius: 3px;
text-decoration: none;
color: #333;
}
/* Comment Form Styling */
.comment-form {
margin-top: 2em;
padding: 2em;
background-color: #f9f9f9;
border: 1px solid #eee;
border-radius: 5px;
}
.comment-form label {
display: block;
margin-bottom: 0.5em;
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: 1em;
border: 1px solid #ccc;
border-radius: 3px;
box-sizing: border-box; /* Important for consistent sizing */
}
.comment-form .submit {
background-color: #0073aa;
color: #fff;
border: none;
padding: 10px 20px;
cursor: pointer;
border-radius: 3px;
}
.comment-form .submit:hover {
background-color: #005177;
}
.comment-form p.comment-form-cookies-consent {
margin-top: 1em;
font-size: 0.9em;
color: #555;
}
Advanced: Customizing wp_list_comments() Callback
For complete control over how each comment is displayed, you can define a custom callback function for wp_list_comments(). This is where you can integrate custom HTML structures, conditionally display elements, or even use block-like rendering for individual comments.
First, define your callback function in functions.php:
function my_theme_comment_callback( $comment, $args, $depth ) { $GLOBALS['comment'] = $comment; $tag = ( $args['style'] === 'ol' ) ? 'li' : 'div'; $classes = ''; if ( $comment->comment_approved == '0' ) { $classes .= ' pending-comment'; } $classes .= ' comment'; $classes .= ' depth-' . $depth; $classes .= ' comment-author-' . get_comment_author(); $classes .= ' comment-by-post-author'; // Add class if comment author is post author if ( $comment->user_id === $comment->post_author ) { $classes .= ' post-author-comment'; } ?> <?php if ( $comment->comment_approved == '0' ) : ?> <em><?php _e( 'Your comment is awaiting moderation.' ); ?></em><br /> <?php endif; ?> <?php switch ( $comment->comment_type ) : case '' : ?> <?php if ( ( $comment->user_id ) && ( $comment->user_id == $comment->post_author ) ) : ?> <li id="comment-<?php comment_ID(); ?>" class="<?php echo $classes; ?>"> <?php else : ?> <li id="comment-<?php comment_ID(); ?>" class="<?php echo $classes; ?>"> <?php endif; ?> <div class="comment-author-vcard"> <?php echo get_avatar( $comment, 50 ); ?> <?php printf( '<cite class="fn">%s</cite>', get_comment_author_link() ); ?> </div><!-- .comment-author-vcard --> <div class="comment-meta commentmetadata"> <a href="<?php echo esc_url( get_comment_link() ); ?>"> <time datetime="<?php comment_date('c'); ?>"> <?php printf( '%1$s at %2$s', get_comment_date(), get_comment_time() ); ?> </time> </a> <?php edit_comment_link( __( '(Edit)' ), ' ', '' ); ?> </div><!-- .comment-meta --> <div class="comment-body"> <?php comment_text(); ?> <div class="reply"> <?php comment_reply_link( array_merge( $args, array( 'add_below' => 'comment', 'depth' => $depth, 'max_depth' => $args['max_depth'] ) ) ); ?> </div><!-- .reply --> </div><!-- .comment-body --> <?php break; case 'pingback' : case 'trackback' : ?> <li id="comment-<?php comment_ID(); ?>" class="<?php echo $classes; ?>"> <?php _e( 'Trackback:' ); ?> <?php comment_author_link(); ?> <?php break; endswitch; ?> <?php if ( $tag === 'div' ) : ?> <div></div> <?php endif; }
Then, in your comments.php file, you’ll call wp_list_comments() with your custom callback:
if ( have_comments() ) { the_title( '<h2 class="comments-title">', '</h2>' ); <?php // Use the custom callback wp_list_comments( 'style' => 'ol', 'callback' => 'my_theme_comment_callback' ); ?> // Pagination if ( get_comment_pages_count() > 1 && get_option( 'page_comments' ) ) { the_comments_pagination( 'prev_text' => '← Previous', 'next_text' => 'Next →' ); } } comment_form(); // Using default arguments here for brevity, but can pass $comments_args as before
Integrating with Gutenberg
While Gutenberg excels at content creation, comment sections are traditionally handled by PHP templates. For a truly Gutenberg-first theme, consider how comments interact with your block system. You might:
- Use a “Comments” block in your theme’s single post template to dynamically insert the comment section. This block would essentially render the content of your
comments.phpfile. - Provide a “Comment Form” block that allows users to customize the form’s appearance and fields within the Site Editor.
- Ensure your CSS for comments is compatible with the editor’s preview, so styles applied in
comments.cssare visible in the Gutenberg editor.
By providing a robust comments.php and leveraging custom callbacks and CSS, you can ensure a consistent and well-styled comment experience that complements your Gutenberg-first theme’s design.