post-meta.php

I’ll build this as a full WordPress theme template part for template-parts/post/post-meta.php, with safe escaping, schema-friendly meta, icons, author/date/category/tags/comments/reading time/edit link, and filter hooks so you can extend it later.

Below is a large, advanced, safe, extendable template-parts/post/post-meta.php file for your RX Theme.

Place it here:

rx-theme/template-parts/post/post-meta.php
<?php
/**
 * Template part: Post Meta
 *
 * File: template-parts/post/post-meta.php
 *
 * Displays post metadata:
 * - Author
 * - Published date
 * - Modified date
 * - Categories
 * - Tags
 * - Comments
 * - Reading time
 * - Views count support
 * - Post format
 * - Sticky label
 * - Edit link
 * - Schema.org microdata
 * - Accessibility-friendly labels
 * - Theme customizer toggles
 * - Developer filter hooks
 *
 * @package RX_Theme
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

$post_id = get_the_ID();

if ( ! $post_id ) {
	return;
}

/**
 * ------------------------------------------------------------
 * Helper: Boolean theme mod with default
 * ------------------------------------------------------------
 */
$rx_meta_show_author        = (bool) get_theme_mod( 'rx_show_post_meta_author', true );
$rx_meta_show_date          = (bool) get_theme_mod( 'rx_show_post_meta_date', true );
$rx_meta_show_modified      = (bool) get_theme_mod( 'rx_show_post_meta_modified_date', true );
$rx_meta_show_categories    = (bool) get_theme_mod( 'rx_show_post_meta_categories', true );
$rx_meta_show_tags          = (bool) get_theme_mod( 'rx_show_post_meta_tags', true );
$rx_meta_show_comments      = (bool) get_theme_mod( 'rx_show_post_meta_comments', true );
$rx_meta_show_reading_time  = (bool) get_theme_mod( 'rx_show_post_meta_reading_time', true );
$rx_meta_show_post_format   = (bool) get_theme_mod( 'rx_show_post_meta_post_format', true );
$rx_meta_show_sticky        = (bool) get_theme_mod( 'rx_show_post_meta_sticky', true );
$rx_meta_show_edit_link     = (bool) get_theme_mod( 'rx_show_post_meta_edit_link', true );
$rx_meta_show_views         = (bool) get_theme_mod( 'rx_show_post_meta_views', false );
$rx_meta_show_word_count    = (bool) get_theme_mod( 'rx_show_post_meta_word_count', false );
$rx_meta_show_reading_level = (bool) get_theme_mod( 'rx_show_post_meta_reading_level', false );

$rx_meta_layout = get_theme_mod( 'rx_post_meta_layout', 'inline' );
$rx_meta_layout = in_array( $rx_meta_layout, array( 'inline', 'stacked', 'compact' ), true ) ? $rx_meta_layout : 'inline';

$rx_meta_icon_style = get_theme_mod( 'rx_post_meta_icon_style', 'emoji' );
$rx_meta_icon_style = in_array( $rx_meta_icon_style, array( 'emoji', 'dashicons', 'none' ), true ) ? $rx_meta_icon_style : 'emoji';

/**
 * Allow developers to control all visibility from one filter.
 */
$rx_meta_settings = apply_filters(
	'rx_theme_post_meta_settings',
	array(
		'show_author'        => $rx_meta_show_author,
		'show_date'          => $rx_meta_show_date,
		'show_modified'      => $rx_meta_show_modified,
		'show_categories'    => $rx_meta_show_categories,
		'show_tags'          => $rx_meta_show_tags,
		'show_comments'      => $rx_meta_show_comments,
		'show_reading_time'  => $rx_meta_show_reading_time,
		'show_post_format'   => $rx_meta_show_post_format,
		'show_sticky'        => $rx_meta_show_sticky,
		'show_edit_link'     => $rx_meta_show_edit_link,
		'show_views'         => $rx_meta_show_views,
		'show_word_count'    => $rx_meta_show_word_count,
		'show_reading_level' => $rx_meta_show_reading_level,
		'layout'             => $rx_meta_layout,
		'icon_style'         => $rx_meta_icon_style,
	),
	$post_id
);

/**
 * ------------------------------------------------------------
 * Helper: Icon output
 * ------------------------------------------------------------
 */
if ( ! function_exists( 'rx_theme_post_meta_icon' ) ) {
	function rx_theme_post_meta_icon( $name = '', $style = 'emoji' ) {
		if ( 'none' === $style ) {
			return '';
		}

		$emoji_icons = array(
			'author'       => '👤',
			'date'         => '📅',
			'modified'     => '🔄',
			'category'     => '📂',
			'tag'          => '🏷️',
			'comment'      => '💬',
			'time'         => '⏱️',
			'format'       => '🧩',
			'sticky'       => '📌',
			'edit'         => '✏️',
			'views'        => '👁️',
			'words'        => '🔤',
			'level'        => '📘',
		);

		$dashicons = array(
			'author'       => 'dashicons-admin-users',
			'date'         => 'dashicons-calendar-alt',
			'modified'     => 'dashicons-update',
			'category'     => 'dashicons-category',
			'tag'          => 'dashicons-tag',
			'comment'      => 'dashicons-admin-comments',
			'time'         => 'dashicons-clock',
			'format'       => 'dashicons-format-aside',
			'sticky'       => 'dashicons-sticky',
			'edit'         => 'dashicons-edit',
			'views'        => 'dashicons-visibility',
			'words'        => 'dashicons-editor-textcolor',
			'level'        => 'dashicons-welcome-learn-more',
		);

		if ( 'dashicons' === $style && isset( $dashicons[ $name ] ) ) {
			return '<span class="rx-meta-icon dashicons ' . esc_attr( $dashicons[ $name ] ) . '" aria-hidden="true"></span>';
		}

		if ( isset( $emoji_icons[ $name ] ) ) {
			return '<span class="rx-meta-icon rx-meta-icon-emoji" aria-hidden="true">' . esc_html( $emoji_icons[ $name ] ) . '</span>';
		}

		return '';
	}
}

/**
 * ------------------------------------------------------------
 * Helper: Calculate reading time
 * ------------------------------------------------------------
 */
if ( ! function_exists( 'rx_theme_get_reading_time' ) ) {
	function rx_theme_get_reading_time( $post_id = 0 ) {
		$post_id = absint( $post_id );

		if ( ! $post_id ) {
			return 0;
		}

		$content = get_post_field( 'post_content', $post_id );
		$content = wp_strip_all_tags( strip_shortcodes( $content ) );

		$word_count       = str_word_count( $content );
		$words_per_minute = absint( apply_filters( 'rx_theme_words_per_minute', 200, $post_id ) );

		if ( $words_per_minute < 1 ) {
			$words_per_minute = 200;
		}

		$reading_time = ceil( $word_count / $words_per_minute );

		return max( 1, absint( $reading_time ) );
	}
}

/**
 * ------------------------------------------------------------
 * Helper: Word count
 * ------------------------------------------------------------
 */
if ( ! function_exists( 'rx_theme_get_word_count' ) ) {
	function rx_theme_get_word_count( $post_id = 0 ) {
		$post_id = absint( $post_id );

		if ( ! $post_id ) {
			return 0;
		}

		$content = get_post_field( 'post_content', $post_id );
		$content = wp_strip_all_tags( strip_shortcodes( $content ) );

		return absint( str_word_count( $content ) );
	}
}

/**
 * ------------------------------------------------------------
 * Helper: Simple reading level
 * Basic estimation only.
 * ------------------------------------------------------------
 */
if ( ! function_exists( 'rx_theme_get_simple_reading_level' ) ) {
	function rx_theme_get_simple_reading_level( $post_id = 0 ) {
		$post_id = absint( $post_id );

		if ( ! $post_id ) {
			return '';
		}

		$content = get_post_field( 'post_content', $post_id );
		$content = wp_strip_all_tags( strip_shortcodes( $content ) );

		$word_count     = str_word_count( $content );
		$sentence_count = preg_match_all( '/[.!?]+/', $content );

		if ( $word_count < 1 || $sentence_count < 1 ) {
			return esc_html__( 'Easy read', 'rx-theme' );
		}

		$average_sentence_length = $word_count / $sentence_count;

		if ( $average_sentence_length <= 12 ) {
			return esc_html__( 'Easy read', 'rx-theme' );
		}

		if ( $average_sentence_length <= 20 ) {
			return esc_html__( 'Standard read', 'rx-theme' );
		}

		return esc_html__( 'Advanced read', 'rx-theme' );
	}
}

/**
 * ------------------------------------------------------------
 * Helper: Views count
 * Works if another plugin/theme function stores post views.
 * Supports common meta keys.
 * ------------------------------------------------------------
 */
if ( ! function_exists( 'rx_theme_get_post_views_count' ) ) {
	function rx_theme_get_post_views_count( $post_id = 0 ) {
		$post_id = absint( $post_id );

		if ( ! $post_id ) {
			return 0;
		}

		$possible_keys = apply_filters(
			'rx_theme_post_views_meta_keys',
			array(
				'rx_post_views',
				'post_views_count',
				'views',
				'_post_views',
				'pvc_views',
			),
			$post_id
		);

		foreach ( $possible_keys as $key ) {
			$value = get_post_meta( $post_id, $key, true );

			if ( '' !== $value && is_numeric( $value ) ) {
				return absint( $value );
			}
		}

		return 0;
	}
}

/**
 * ------------------------------------------------------------
 * Helper: Separator
 * ------------------------------------------------------------
 */
$rx_meta_separator = apply_filters(
	'rx_theme_post_meta_separator',
	'<span class="rx-post-meta-separator" aria-hidden="true">•</span>',
	$post_id
);

/**
 * ------------------------------------------------------------
 * Build meta items
 * ------------------------------------------------------------
 */
$rx_meta_items = array();

/**
 * Sticky label
 */
if ( ! empty( $rx_meta_settings['show_sticky'] ) && is_sticky( $post_id ) ) {
	$rx_meta_items['sticky'] = sprintf(
		'<span class="rx-post-meta-item rx-post-meta-sticky">%1$s<span class="rx-post-meta-text">%2$s</span></span>',
		rx_theme_post_meta_icon( 'sticky', $rx_meta_settings['icon_style'] ),
		esc_html__( 'Featured', 'rx-theme' )
	);
}

/**
 * Author
 */
if ( ! empty( $rx_meta_settings['show_author'] ) ) {
	$author_id   = absint( get_post_field( 'post_author', $post_id ) );
	$author_name = get_the_author_meta( 'display_name', $author_id );
	$author_url  = get_author_posts_url( $author_id );

	if ( $author_name ) {
		$rx_meta_items['author'] = sprintf(
			'<span class="rx-post-meta-item rx-post-meta-author" itemprop="author" itemscope itemtype="https://schema.org/Person">%1$s<span class="rx-screen-reader-text">%2$s </span><a class="rx-post-meta-link rx-author-link" href="%3$s" rel="author" itemprop="url"><span itemprop="name">%4$s</span></a></span>',
			rx_theme_post_meta_icon( 'author', $rx_meta_settings['icon_style'] ),
			esc_html__( 'Written by', 'rx-theme' ),
			esc_url( $author_url ),
			esc_html( $author_name )
		);
	}
}

/**
 * Published date
 */
if ( ! empty( $rx_meta_settings['show_date'] ) ) {
	$published_time_iso = get_the_date( DATE_W3C, $post_id );
	$published_time     = get_the_date( '', $post_id );

	$rx_meta_items['date'] = sprintf(
		'<span class="rx-post-meta-item rx-post-meta-date">%1$s<span class="rx-screen-reader-text">%2$s </span><a class="rx-post-meta-link rx-date-link" href="%3$s" rel="bookmark"><time datetime="%4$s" itemprop="datePublished">%5$s</time></a></span>',
		rx_theme_post_meta_icon( 'date', $rx_meta_settings['icon_style'] ),
		esc_html__( 'Published on', 'rx-theme' ),
		esc_url( get_permalink( $post_id ) ),
		esc_attr( $published_time_iso ),
		esc_html( $published_time )
	);
}

/**
 * Modified date
 */
if ( ! empty( $rx_meta_settings['show_modified'] ) ) {
	$published_timestamp = get_the_time( 'U', $post_id );
	$modified_timestamp  = get_the_modified_time( 'U', $post_id );

	if ( $modified_timestamp && $modified_timestamp > $published_timestamp ) {
		$modified_time_iso = get_the_modified_date( DATE_W3C, $post_id );
		$modified_time     = get_the_modified_date( '', $post_id );

		$rx_meta_items['modified'] = sprintf(
			'<span class="rx-post-meta-item rx-post-meta-modified">%1$s<span class="rx-screen-reader-text">%2$s </span><time datetime="%3$s" itemprop="dateModified">%4$s</time></span>',
			rx_theme_post_meta_icon( 'modified', $rx_meta_settings['icon_style'] ),
			esc_html__( 'Updated on', 'rx-theme' ),
			esc_attr( $modified_time_iso ),
			esc_html( $modified_time )
		);
	}
}

/**
 * Categories
 */
if ( ! empty( $rx_meta_settings['show_categories'] ) ) {
	$categories_list = get_the_category_list(
		esc_html_x( ', ', 'category separator', 'rx-theme' ),
		'',
		$post_id
	);

	if ( $categories_list ) {
		$rx_meta_items['categories'] = sprintf(
			'<span class="rx-post-meta-item rx-post-meta-categories">%1$s<span class="rx-screen-reader-text">%2$s </span><span class="rx-post-meta-tax-links">%3$s</span></span>',
			rx_theme_post_meta_icon( 'category', $rx_meta_settings['icon_style'] ),
			esc_html__( 'Categories:', 'rx-theme' ),
			wp_kses_post( $categories_list )
		);
	}
}

/**
 * Tags
 */
if ( ! empty( $rx_meta_settings['show_tags'] ) ) {
	$tags_list = get_the_tag_list(
		'',
		esc_html_x( ', ', 'tag separator', 'rx-theme' ),
		'',
		$post_id
	);

	if ( $tags_list ) {
		$rx_meta_items['tags'] = sprintf(
			'<span class="rx-post-meta-item rx-post-meta-tags">%1$s<span class="rx-screen-reader-text">%2$s </span><span class="rx-post-meta-tax-links">%3$s</span></span>',
			rx_theme_post_meta_icon( 'tag', $rx_meta_settings['icon_style'] ),
			esc_html__( 'Tags:', 'rx-theme' ),
			wp_kses_post( $tags_list )
		);
	}
}

/**
 * Comments
 */
if ( ! empty( $rx_meta_settings['show_comments'] ) && ! post_password_required( $post_id ) && comments_open( $post_id ) || get_comments_number( $post_id ) ) {
	$comments_number = get_comments_number( $post_id );

	if ( 0 === absint( $comments_number ) ) {
		$comments_text = esc_html__( 'No comments', 'rx-theme' );
	} elseif ( 1 === absint( $comments_number ) ) {
		$comments_text = esc_html__( '1 comment', 'rx-theme' );
	} else {
		$comments_text = sprintf(
			/* translators: %s: number of comments */
			esc_html__( '%s comments', 'rx-theme' ),
			number_format_i18n( $comments_number )
		);
	}

	$rx_meta_items['comments'] = sprintf(
		'<span class="rx-post-meta-item rx-post-meta-comments">%1$s<a class="rx-post-meta-link rx-comments-link" href="%2$s">%3$s</a></span>',
		rx_theme_post_meta_icon( 'comment', $rx_meta_settings['icon_style'] ),
		esc_url( get_comments_link( $post_id ) ),
		esc_html( $comments_text )
	);
}

/**
 * Reading time
 */
if ( ! empty( $rx_meta_settings['show_reading_time'] ) ) {
	$reading_time = rx_theme_get_reading_time( $post_id );

	$rx_meta_items['reading_time'] = sprintf(
		'<span class="rx-post-meta-item rx-post-meta-reading-time">%1$s<span class="rx-post-meta-text">%2$s</span></span>',
		rx_theme_post_meta_icon( 'time', $rx_meta_settings['icon_style'] ),
		sprintf(
			/* translators: %s: reading time in minutes */
			esc_html( _n( '%s min read', '%s mins read', $reading_time, 'rx-theme' ) ),
			number_format_i18n( $reading_time )
		)
	);
}

/**
 * Word count
 */
if ( ! empty( $rx_meta_settings['show_word_count'] ) ) {
	$word_count = rx_theme_get_word_count( $post_id );

	if ( $word_count > 0 ) {
		$rx_meta_items['word_count'] = sprintf(
			'<span class="rx-post-meta-item rx-post-meta-word-count">%1$s<span class="rx-post-meta-text">%2$s</span></span>',
			rx_theme_post_meta_icon( 'words', $rx_meta_settings['icon_style'] ),
			sprintf(
				/* translators: %s: word count */
				esc_html__( '%s words', 'rx-theme' ),
				number_format_i18n( $word_count )
			)
		);
	}
}

/**
 * Simple reading level
 */
if ( ! empty( $rx_meta_settings['show_reading_level'] ) ) {
	$reading_level = rx_theme_get_simple_reading_level( $post_id );

	if ( $reading_level ) {
		$rx_meta_items['reading_level'] = sprintf(
			'<span class="rx-post-meta-item rx-post-meta-reading-level">%1$s<span class="rx-post-meta-text">%2$s</span></span>',
			rx_theme_post_meta_icon( 'level', $rx_meta_settings['icon_style'] ),
			esc_html( $reading_level )
		);
	}
}

/**
 * Post views
 */
if ( ! empty( $rx_meta_settings['show_views'] ) ) {
	$views_count = rx_theme_get_post_views_count( $post_id );

	if ( $views_count > 0 ) {
		$rx_meta_items['views'] = sprintf(
			'<span class="rx-post-meta-item rx-post-meta-views">%1$s<span class="rx-post-meta-text">%2$s</span></span>',
			rx_theme_post_meta_icon( 'views', $rx_meta_settings['icon_style'] ),
			sprintf(
				/* translators: %s: views count */
				esc_html__( '%s views', 'rx-theme' ),
				number_format_i18n( $views_count )
			)
		);
	}
}

/**
 * Post format
 */
if ( ! empty( $rx_meta_settings['show_post_format'] ) && current_theme_supports( 'post-formats' ) ) {
	$post_format = get_post_format( $post_id );

	if ( $post_format ) {
		$post_format_object = get_post_format_object( $post_format );

		if ( $post_format_object && ! empty( $post_format_object->labels->singular_name ) ) {
			$rx_meta_items['post_format'] = sprintf(
				'<span class="rx-post-meta-item rx-post-meta-format">%1$s<span class="rx-screen-reader-text">%2$s </span><span class="rx-post-meta-text">%3$s</span></span>',
				rx_theme_post_meta_icon( 'format', $rx_meta_settings['icon_style'] ),
				esc_html__( 'Post format:', 'rx-theme' ),
				esc_html( $post_format_object->labels->singular_name )
			);
		}
	}
}

/**
 * Edit link
 */
if ( ! empty( $rx_meta_settings['show_edit_link'] ) && current_user_can( 'edit_post', $post_id ) ) {
	$edit_url = get_edit_post_link( $post_id );

	if ( $edit_url ) {
		$rx_meta_items['edit'] = sprintf(
			'<span class="rx-post-meta-item rx-post-meta-edit">%1$s<a class="rx-post-meta-link rx-edit-link" href="%2$s">%3$s</a></span>',
			rx_theme_post_meta_icon( 'edit', $rx_meta_settings['icon_style'] ),
			esc_url( $edit_url ),
			esc_html__( 'Edit', 'rx-theme' )
		);
	}
}

/**
 * Allow developers to modify meta items.
 */
$rx_meta_items = apply_filters( 'rx_theme_post_meta_items', $rx_meta_items, $post_id, $rx_meta_settings );

/**
 * Stop if nothing to show.
 */
if ( empty( $rx_meta_items ) || ! is_array( $rx_meta_items ) ) {
	return;
}

$rx_meta_classes = array(
	'rx-post-meta',
	'rx-post-meta-layout-' . sanitize_html_class( $rx_meta_settings['layout'] ),
	'rx-post-meta-icons-' . sanitize_html_class( $rx_meta_settings['icon_style'] ),
);

$rx_meta_classes = apply_filters( 'rx_theme_post_meta_classes', $rx_meta_classes, $post_id, $rx_meta_settings );

?>

<div
	class="<?php echo esc_attr( implode( ' ', array_map( 'sanitize_html_class', $rx_meta_classes ) ) ); ?>"
	aria-label="<?php echo esc_attr__( 'Post information', 'rx-theme' ); ?>"
>
	<?php
	/**
	 * Before post meta hook.
	 */
	do_action( 'rx_theme_before_post_meta', $post_id, $rx_meta_settings );

	$rx_meta_output = array();

	foreach ( $rx_meta_items as $rx_meta_key => $rx_meta_html ) {
		if ( empty( $rx_meta_html ) ) {
			continue;
		}

		$rx_meta_output[] = apply_filters(
			'rx_theme_post_meta_item_html',
			$rx_meta_html,
			$rx_meta_key,
			$post_id,
			$rx_meta_settings
		);
	}

	echo wp_kses_post( implode( $rx_meta_separator, $rx_meta_output ) );

	/**
	 * After post meta hook.
	 */
	do_action( 'rx_theme_after_post_meta', $post_id, $rx_meta_settings );
	?>
</div>

Important small fix

In the comments condition, replace this line:

if ( ! empty( $rx_meta_settings['show_comments'] ) && ! post_password_required( $post_id ) && comments_open( $post_id ) || get_comments_number( $post_id ) ) {

with this safer version:

if ( ! empty( $rx_meta_settings['show_comments'] ) && ! post_password_required( $post_id ) && ( comments_open( $post_id ) || get_comments_number( $post_id ) ) ) {

So the final comments block begins like this:

if ( ! empty( $rx_meta_settings['show_comments'] ) && ! post_password_required( $post_id ) && ( comments_open( $post_id ) || get_comments_number( $post_id ) ) ) {

How to call it in your post template

Use this inside your post template file, such as:

get_template_part( 'template-parts/post/post-meta' );

Example inside:

<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
	<header class="entry-header">
		<?php the_title( '<h1 class="entry-title">', '</h1>' ); ?>

		<?php get_template_part( 'template-parts/post/post-meta' ); ?>
	</header>

	<div class="entry-content">
		<?php the_content(); ?>
	</div>
</article>

Optional CSS

Add this to your theme CSS:

.rx-post-meta {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 6px;
	font-size: 14px;
	line-height: 1.6;
	color: #666;
	margin-top: 10px;
	margin-bottom: 15px;
}

.rx-post-meta-layout-stacked {
	display: block;
}

.rx-post-meta-layout-stacked .rx-post-meta-item {
	display: block;
	margin-bottom: 6px;
}

.rx-post-meta-layout-compact {
	font-size: 13px;
	gap: 4px;
}

.rx-post-meta-item {
	display: inline-flex;
	align-items: center;
	gap: 4px;
}

.rx-post-meta a {
	text-decoration: none;
	color: inherit;
}

.rx-post-meta a:hover,
.rx-post-meta a:focus {
	text-decoration: underline;
}

.rx-post-meta-separator {
	opacity: 0.5;
	margin-left: 4px;
	margin-right: 4px;
}

.rx-meta-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	line-height: 1;
}

.rx-meta-icon-emoji {
	font-size: 14px;
}

.rx-post-meta-sticky {
	font-weight: 700;
}

.rx-post-meta-edit a {
	font-weight: 600;
}

.rx-screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

This file is safe, expandable, and will not break if some functions or data are missing.

Leave a Reply

Your email address will not be published. Required fields are marked *