Classic Theme Comments and Discussion
SkillCommunicationBuild or audit classic theme comments output for WP 7.1. Covers `comments_template()`, `comments.php`, `post_password_required()`, `have_comments()`, `wp_list_comments()`, comment pagination, `comment_form()`, threaded comment reply script loading, closed-comment messaging, accessible comment navigation, excluding private editor Notes, escaping comment titles and labels, and common mistakes such as custom comment forms, missing password guards, loading `comment-reply` globally, broken callback walkers, or showing comments on unsupported post types.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Classic Theme Comments and Discussion skill
What this skill tells your AI
The instructions your AI receives, as published by lonsdale201/wp-agent-skills in theme-development/classic-theme-comments-discussion/SKILL.md and read by ahel’s review.
Use this when adding or reviewing comments in a classic PHP theme: comments.php, comment list markup, comment pagination, the comment form, closed-comment states, and threaded reply behavior.
When to Use This Skill
- Creating or editing
comments.php. - Calling
comments_template()fromsingle.php,page.php, or a singular template part. - Styling
wp_list_comments()output or replacing a comment callback. - Loading
comment-reply. - Fixing missing comment pagination, password-protected post leaks, or inaccessible comment navigation.
Call Comments From Singular Templates
In a singular template, call comments after the content when the post supports discussion.
if ( comments_open() || get_comments_number() ) {
comments_template();
}
Rules:
- Do not call
comments_template()on archives or search templates. - Check
comments_open()orget_comments_number()before loading the template. - If a custom post type does not support comments, do not force comments into its template.
- Keep the rendering code in
comments.php. - Editor Notes use the comments table but are private editorial data. Do not use
type => allin custom public queries or walkers; keep public theme output to ordinary comments/pings intentionally.
comments_template() is designed for single/page contexts and returns early outside supported contexts unless $withcomments is set.
comments.php Skeleton
Start with the password guard. This prevents comment content from leaking on protected posts.
<?php
if ( post_password_required() ) {
return;
}
?>
<section id="comments" class="comments-area" aria-label="<?php esc_attr_e( 'Comments', 'textdomain' ); ?>">
<?php if ( have_comments() ) : ?>
<h2 class="comments-title">
<?php
$comment_count = get_comments_number();
printf(
esc_html(
_nx(
'%1$s comment on "%2$s"',
'%1$s comments on "%2$s"',
$comment_count,
'comments title',
'textdomain'
)
),
esc_html( number_format_i18n( $comment_count ) ),
esc_html( get_the_title() )
);
?>
</h2>
<ol class="comment-list">
<?php
wp_list_comments(
array(
'style' => 'ol',
'short_ping' => true,
'avatar_size' => 48,
)
);
?>
</ol>
<?php endif; ?>
<?php comment_form(); ?>
</section>
Rules:
- Use
post_password_required()before output. - Use
have_comments()for the list state. - Use
number_format_i18n()for counts. - Escape the post title when inserting it into a comment heading.
- Use
comment_form()unless there is a very specific reason not to.
Comment Pagination
If comment pagination is enabled, render navigation before and/or after the list.
if ( get_comment_pages_count() > 1 && get_option( 'page_comments' ) ) :
?>
<nav class="comment-navigation" aria-label="<?php esc_attr_e( 'Comment navigation', 'textdomain' ); ?>">
<h2 class="screen-reader-text"><?php esc_html_e( 'Comment navigation', 'textdomain' ); ?></h2>
<div class="nav-previous"><?php previous_comments_link( esc_html__( 'Older comments', 'textdomain' ) ); ?></div>
<div class="nav-next"><?php next_comments_link( esc_html__( 'Newer comments', 'textdomain' ) ); ?></div>
</nav>
<?php
endif;
Rules:
- Only show comment navigation when there is more than one comment page.
- Respect the
page_commentsoption. - Give comment navigation an accessible name.
- Do not build comment-page URLs manually.
Comment List Output
Prefer core comment output unless the design truly needs custom markup.
wp_list_comments(
array(
'style' => 'ol',
'short_ping' => true,
'avatar_size' => 48,
'format' => 'html5',
)
);
Rules:
style => 'ol'should match an ordered-list wrapper.- Use
format => 'html5'if the theme supports HTML5 comment markup. - Avoid custom callbacks/walkers for simple class or avatar-size changes.
- If using a callback, escape author links, dates, edit links, and custom fields.
- Preserve reply links and moderation notices.
Comment Form
Use comment_form() for the public comment form.
comment_form(
array(
'title_reply_before' => '<h2 id="reply-title" class="comment-reply-title">',
'title_reply_after' => '</h2>',
)
);
Rules:
- Do not hand-code a replacement form unless you are intentionally replacing core behavior.
- Core handles logged-in state, required fields, cookies consent, form action, hidden comment fields, and closed comments.
- If overriding fields, keep labels,
requiredstate, autocomplete attributes, and cookies consent behavior. - Escape any custom labels or descriptions.
Threaded Replies
Load comment-reply only when it can be used.
add_action( 'wp_enqueue_scripts', 'mytheme_enqueue_comment_reply' );
function mytheme_enqueue_comment_reply() {
if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
wp_enqueue_script( 'comment-reply' );
}
}
Rules:
- Do not enqueue
comment-replyglobally. - The script belongs on
wp_enqueue_scripts, not insidecomments.php. - Threaded comments also need compatible
wp_list_comments()output and reply links.
Closed Comments
Show a closed-comments message only when useful.
if ( ! comments_open() && get_comments_number() && post_type_supports( get_post_type(), 'comments' ) ) :
?>
<p class="no-comments"><?php esc_html_e( 'Comments are closed.', 'textdomain' ); ?></p>
<?php
endif;
Do not show "Comments are closed" on every page with no discussion.
Review Checklist
- Singular templates call
comments_template()only when needed. comments.phpreturns early forpost_password_required().- Comment count headings are pluralized and escaped.
- Comment pagination respects
page_comments. wp_list_comments()is used instead of raw comment loops.comment_form()is used instead of a hand-built form.comment-replyis conditionally enqueued.- Closed-comment messaging is not noisy.
- Custom callbacks preserve reply links, moderation state, and escaping.
- Custom comment queries do not expose
comment_type = note.
Common Mistakes
- Forgetting the password-protected post guard.
- Building a custom public comment form and losing core hidden fields/consent behavior.
- Loading
comment-replyon every frontend request. - Rendering comment navigation when comments are not paginated.
- Echoing
get_the_title()raw inside the comments heading. - Using a
<div>wrapper while askingwp_list_comments()forstyle => 'ol'. - Querying all comment types and leaking editor Notes into public output.
References
- Official documentation: https://developer.wordpress.org/themes/classic-themes/templates/partial-and-miscellaneous-template-files/comment-template/
- Official documentation: https://developer.wordpress.org/reference/functions/comment_form/
- Official documentation: https://developer.wordpress.org/reference/functions/wp_list_comments/
- Related skill:
wordpress/wp-comments-notes-apifor Notes, notification, REST, and ping behavior. - Verified source paths:
wp-includes/comment-template.phpwp-includes/script-loader.phpwp-content/themes/storefront/comments.phpwp-content/themes/generatepress/comments.php
Signals
- GitHub stars
- 22
- Forks
- 2
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
classic-theme-comments-discussion- Source
- github.com/lonsdale201/wp-agent-skills