Classic Theme Comments and Discussion

SkillCommunication

Build 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.

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() from single.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() or get_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 => all in 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_comments option.
  • 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, required state, 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-reply globally.
  • The script belongs on wp_enqueue_scripts, not inside comments.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.php returns early for post_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-reply is 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-reply on 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 asking wp_list_comments() for style => 'ol'.
  • Querying all comment types and leaking editor Notes into public output.

References

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