I’ll build this as a powerful drop-in template-parts/post/related-posts.php file for your RX Theme, using safe WordPress functions, caching, taxonomy matching, author/date fallback, thumbnails, schema, accessibility, and responsive markup.
Below is an advanced, long, feature-rich template-parts/post/related-posts.php file for your RX Theme.
It includes: category/tag/custom taxonomy matching, fallback posts, popular/commented/random fallback, transient cache, accessibility, schema JSON-LD, thumbnail fallback, reading time, post meta, excerpt trimming, responsive class names, safe escaping, filters for future expansion, and proper wp_reset_postdata() after custom WP_Query. WordPress officially recommends wp_reset_postdata() after secondary loops, and get_the_terms() is the correct function for retrieving post taxonomy terms.
Create this file:
rx-theme/template-parts/post/related-posts.php
Then paste this full code:
<?php
/**
* Template Part: Related Posts
*
* File path:
* template-parts/post/related-posts.php
*
* RX Theme Advanced Related Posts System
*
* Features:
* - Related posts by category, tag, and custom taxonomies
* - Fallback query if no related posts found
* - Optional popular posts fallback by comments
* - Optional random fallback
* - Transient caching for performance
* - Thumbnail fallback support
* - Reading time support
* - Excerpt support
* - Author/date/category/comment meta
* - Schema JSON-LD ItemList
* - Accessibility-friendly markup
* - Developer filters for future expansion
*
* Usage in single.php:
*
* get_template_part( 'template-parts/post/related-posts' );
*
* @package RX_Theme
*/
defined( 'ABSPATH' ) || exit;
if ( ! is_singular( 'post' ) ) {
return;
}
$current_post_id = get_the_ID();
if ( ! $current_post_id ) {
return;
}
/**
* ------------------------------------------------------------
* 1. Basic Settings
* ------------------------------------------------------------
*/
$rx_related_defaults = array(
'enabled' => true,
'title' => __( 'Related Articles', 'rx-theme' ),
'subtitle' => __( 'You may also like these carefully selected posts.', 'rx-theme' ),
'posts_per_page' => 6,
'columns' => 3,
'excerpt_length' => 22,
'show_thumbnail' => true,
'show_category' => true,
'show_date' => true,
'show_author' => true,
'show_comments' => true,
'show_excerpt' => true,
'show_reading_time' => true,
'show_badge' => true,
'show_schema' => true,
'cache_enabled' => true,
'cache_expiration' => 12 * HOUR_IN_SECONDS,
'fallback_enabled' => true,
'fallback_orderby' => 'date',
'random_fallback' => false,
'popular_fallback' => true,
'thumbnail_size' => 'medium_large',
'default_thumbnail_url' => '',
'section_id' => 'rx-related-posts',
'card_style' => 'default',
'open_links_new_tab' => false,
'include_sticky_posts' => false,
);
/**
* Filter: rx_related_posts_settings
*
* Example:
* add_filter( 'rx_related_posts_settings', function( $settings ) {
* $settings['posts_per_page'] = 8;
* $settings['columns'] = 4;
* return $settings;
* });
*/
$rx_related_settings = apply_filters(
'rx_related_posts_settings',
$rx_related_defaults,
$current_post_id
);
if ( empty( $rx_related_settings['enabled'] ) ) {
return;
}
$rx_posts_per_page = absint( $rx_related_settings['posts_per_page'] );
if ( $rx_posts_per_page < 1 ) {
$rx_posts_per_page = 6;
}
$rx_columns = absint( $rx_related_settings['columns'] );
if ( $rx_columns < 1 || $rx_columns > 6 ) {
$rx_columns = 3;
}
/**
* ------------------------------------------------------------
* 2. Helper Functions
* ------------------------------------------------------------
*/
if ( ! function_exists( 'rx_related_posts_get_term_ids' ) ) {
/**
* Get term IDs from a post taxonomy.
*
* @param int $post_id Post ID.
* @param string $taxonomy Taxonomy name.
* @return array
*/
function rx_related_posts_get_term_ids( $post_id, $taxonomy ) {
$post_id = absint( $post_id );
$taxonomy = sanitize_key( $taxonomy );
if ( ! $post_id || ! taxonomy_exists( $taxonomy ) ) {
return array();
}
$terms = get_the_terms( $post_id, $taxonomy );
if ( empty( $terms ) || is_wp_error( $terms ) ) {
return array();
}
$term_ids = wp_list_pluck( $terms, 'term_id' );
$term_ids = array_map( 'absint', $term_ids );
$term_ids = array_filter( $term_ids );
return array_values( array_unique( $term_ids ) );
}
}
if ( ! function_exists( 'rx_related_posts_trim_excerpt' ) ) {
/**
* Create safe trimmed excerpt.
*
* @param int $post_id Post ID.
* @param int $length Word length.
* @return string
*/
function rx_related_posts_trim_excerpt( $post_id, $length = 22 ) {
$post_id = absint( $post_id );
$length = absint( $length );
if ( ! $post_id ) {
return '';
}
if ( $length < 5 ) {
$length = 22;
}
$manual_excerpt = get_the_excerpt( $post_id );
if ( ! empty( $manual_excerpt ) ) {
return wp_trim_words( wp_strip_all_tags( $manual_excerpt ), $length, '…' );
}
$content = get_post_field( 'post_content', $post_id );
$content = strip_shortcodes( $content );
$content = wp_strip_all_tags( $content );
return wp_trim_words( $content, $length, '…' );
}
}
if ( ! function_exists( 'rx_related_posts_reading_time' ) ) {
/**
* Calculate estimated reading time.
*
* @param int $post_id Post ID.
* @return string
*/
function rx_related_posts_reading_time( $post_id ) {
$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 );
$minutes = max( 1, ceil( $word_count / 200 ) );
return sprintf(
/* translators: %s: reading time minutes */
_n( '%s min read', '%s mins read', $minutes, 'rx-theme' ),
number_format_i18n( $minutes )
);
}
}
if ( ! function_exists( 'rx_related_posts_primary_category' ) ) {
/**
* Get first category for display.
*
* @param int $post_id Post ID.
* @return WP_Term|null
*/
function rx_related_posts_primary_category( $post_id ) {
$post_id = absint( $post_id );
if ( ! $post_id ) {
return null;
}
$categories = get_the_category( $post_id );
if ( empty( $categories ) || is_wp_error( $categories ) ) {
return null;
}
return $categories[0];
}
}
if ( ! function_exists( 'rx_related_posts_thumbnail' ) ) {
/**
* Render related post thumbnail.
*
* @param int $post_id Post ID.
* @param string $size Image size.
* @param string $default_thumbnail_url Default image URL.
* @return string
*/
function rx_related_posts_thumbnail( $post_id, $size = 'medium_large', $default_thumbnail_url = '' ) {
$post_id = absint( $post_id );
$size = sanitize_key( $size );
if ( ! $post_id ) {
return '';
}
if ( has_post_thumbnail( $post_id ) ) {
return get_the_post_thumbnail(
$post_id,
$size,
array(
'class' => 'rx-related-posts__image',
'loading' => 'lazy',
'alt' => esc_attr( get_the_title( $post_id ) ),
)
);
}
if ( ! empty( $default_thumbnail_url ) ) {
return sprintf(
'<img class="rx-related-posts__image rx-related-posts__image--fallback" src="%1$s" alt="%2$s" loading="lazy" />',
esc_url( $default_thumbnail_url ),
esc_attr( get_the_title( $post_id ) )
);
}
return sprintf(
'<div class="rx-related-posts__image-placeholder" aria-hidden="true">
<span class="rx-related-posts__image-placeholder-icon">RX</span>
</div>'
);
}
}
if ( ! function_exists( 'rx_related_posts_cache_key' ) ) {
/**
* Build transient cache key.
*
* @param int $post_id Post ID.
* @param array $settings Settings.
* @return string
*/
function rx_related_posts_cache_key( $post_id, $settings ) {
$post_id = absint( $post_id );
$key_data = array(
'post_id' => $post_id,
'posts_per_page' => isset( $settings['posts_per_page'] ) ? absint( $settings['posts_per_page'] ) : 6,
'columns' => isset( $settings['columns'] ) ? absint( $settings['columns'] ) : 3,
'fallback' => ! empty( $settings['fallback_enabled'] ),
'popular' => ! empty( $settings['popular_fallback'] ),
'random' => ! empty( $settings['random_fallback'] ),
);
return 'rx_related_posts_' . md5( wp_json_encode( $key_data ) );
}
}
/**
* ------------------------------------------------------------
* 3. Build Taxonomy Query
* ------------------------------------------------------------
*/
$rx_category_ids = rx_related_posts_get_term_ids( $current_post_id, 'category' );
$rx_tag_ids = rx_related_posts_get_term_ids( $current_post_id, 'post_tag' );
/**
* Add custom taxonomies if your theme uses them.
*
* Example custom taxonomies:
* - disease_category
* - topic
* - medical_specialty
* - rx_condition
*/
$rx_custom_taxonomies = apply_filters(
'rx_related_posts_custom_taxonomies',
array(),
$current_post_id
);
$rx_tax_query = array(
'relation' => 'OR',
);
if ( ! empty( $rx_category_ids ) ) {
$rx_tax_query[] = array(
'taxonomy' => 'category',
'field' => 'term_id',
'terms' => $rx_category_ids,
'include_children' => true,
'operator' => 'IN',
);
}
if ( ! empty( $rx_tag_ids ) ) {
$rx_tax_query[] = array(
'taxonomy' => 'post_tag',
'field' => 'term_id',
'terms' => $rx_tag_ids,
'include_children' => false,
'operator' => 'IN',
);
}
if ( ! empty( $rx_custom_taxonomies ) && is_array( $rx_custom_taxonomies ) ) {
foreach ( $rx_custom_taxonomies as $rx_taxonomy ) {
$rx_taxonomy = sanitize_key( $rx_taxonomy );
if ( ! taxonomy_exists( $rx_taxonomy ) ) {
continue;
}
$rx_term_ids = rx_related_posts_get_term_ids( $current_post_id, $rx_taxonomy );
if ( empty( $rx_term_ids ) ) {
continue;
}
$rx_tax_query[] = array(
'taxonomy' => $rx_taxonomy,
'field' => 'term_id',
'terms' => $rx_term_ids,
'include_children' => true,
'operator' => 'IN',
);
}
}
$rx_has_tax_query = count( $rx_tax_query ) > 1;
/**
* ------------------------------------------------------------
* 4. Query Related Posts
* ------------------------------------------------------------
*/
$rx_cache_key = rx_related_posts_cache_key( $current_post_id, $rx_related_settings );
$rx_related_posts = false;
if ( ! empty( $rx_related_settings['cache_enabled'] ) ) {
$rx_related_posts = get_transient( $rx_cache_key );
}
if ( false === $rx_related_posts ) {
$rx_related_posts = array();
$rx_base_args = array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => $rx_posts_per_page,
'post__not_in' => array( $current_post_id ),
'ignore_sticky_posts' => empty( $rx_related_settings['include_sticky_posts'] ),
'no_found_rows' => true,
'update_post_meta_cache' => true,
'update_post_term_cache' => true,
);
if ( $rx_has_tax_query ) {
$rx_related_args = array_merge(
$rx_base_args,
array(
'tax_query' => $rx_tax_query,
'orderby' => array(
'date' => 'DESC',
'comment_count' => 'DESC',
),
)
);
/**
* Filter: rx_related_posts_query_args
*/
$rx_related_args = apply_filters(
'rx_related_posts_query_args',
$rx_related_args,
$current_post_id,
$rx_related_settings
);
$rx_related_query = new WP_Query( $rx_related_args );
if ( $rx_related_query->have_posts() ) {
foreach ( $rx_related_query->posts as $rx_post_object ) {
$rx_related_posts[] = absint( $rx_post_object->ID );
}
}
wp_reset_postdata();
}
/**
* ------------------------------------------------------------
* 5. Fallback Query
* ------------------------------------------------------------
*/
if (
! empty( $rx_related_settings['fallback_enabled'] )
&& count( $rx_related_posts ) < $rx_posts_per_page
) {
$rx_needed_posts = $rx_posts_per_page - count( $rx_related_posts );
$rx_exclude_posts = array_merge(
array( $current_post_id ),
$rx_related_posts
);
$rx_fallback_orderby = sanitize_key( $rx_related_settings['fallback_orderby'] );
if ( ! empty( $rx_related_settings['random_fallback'] ) ) {
$rx_fallback_orderby = 'rand';
}
$rx_fallback_args = array_merge(
$rx_base_args,
array(
'posts_per_page' => $rx_needed_posts,
'post__not_in' => array_map( 'absint', $rx_exclude_posts ),
'orderby' => $rx_fallback_orderby,
'order' => 'DESC',
)
);
if ( ! empty( $rx_related_settings['popular_fallback'] ) ) {
$rx_fallback_args['orderby'] = 'comment_count';
}
/**
* Filter: rx_related_posts_fallback_query_args
*/
$rx_fallback_args = apply_filters(
'rx_related_posts_fallback_query_args',
$rx_fallback_args,
$current_post_id,
$rx_related_posts,
$rx_related_settings
);
$rx_fallback_query = new WP_Query( $rx_fallback_args );
if ( $rx_fallback_query->have_posts() ) {
foreach ( $rx_fallback_query->posts as $rx_post_object ) {
$rx_related_posts[] = absint( $rx_post_object->ID );
}
}
wp_reset_postdata();
}
$rx_related_posts = array_values( array_unique( array_filter( array_map( 'absint', $rx_related_posts ) ) ) );
if ( ! empty( $rx_related_settings['cache_enabled'] ) ) {
set_transient(
$rx_cache_key,
$rx_related_posts,
absint( $rx_related_settings['cache_expiration'] )
);
}
}
/**
* Final filter before rendering.
*/
$rx_related_posts = apply_filters(
'rx_related_posts_ids',
$rx_related_posts,
$current_post_id,
$rx_related_settings
);
if ( empty( $rx_related_posts ) || ! is_array( $rx_related_posts ) ) {
return;
}
/**
* ------------------------------------------------------------
* 6. Markup Settings
* ------------------------------------------------------------
*/
$rx_section_id = ! empty( $rx_related_settings['section_id'] )
? sanitize_html_class( $rx_related_settings['section_id'] )
: 'rx-related-posts';
$rx_card_style = ! empty( $rx_related_settings['card_style'] )
? sanitize_html_class( $rx_related_settings['card_style'] )
: 'default';
$rx_target = ! empty( $rx_related_settings['open_links_new_tab'] ) ? ' target="_blank"' : '';
$rx_rel = ! empty( $rx_related_settings['open_links_new_tab'] ) ? ' rel="noopener noreferrer"' : '';
$rx_section_classes = array(
'rx-related-posts',
'rx-related-posts--columns-' . $rx_columns,
'rx-related-posts--style-' . $rx_card_style,
);
$rx_section_classes = apply_filters(
'rx_related_posts_section_classes',
$rx_section_classes,
$current_post_id,
$rx_related_settings
);
$rx_section_class = implode( ' ', array_map( 'sanitize_html_class', $rx_section_classes ) );
/**
* ------------------------------------------------------------
* 7. Schema Data
* ------------------------------------------------------------
*/
$rx_schema_items = array();
if ( ! empty( $rx_related_settings['show_schema'] ) ) {
$rx_schema_position = 1;
foreach ( $rx_related_posts as $rx_related_post_id ) {
$rx_schema_items[] = array(
'@type' => 'ListItem',
'position' => $rx_schema_position,
'url' => get_permalink( $rx_related_post_id ),
'name' => wp_strip_all_tags( get_the_title( $rx_related_post_id ) ),
);
$rx_schema_position++;
}
}
?>
<section
id="<?php echo esc_attr( $rx_section_id ); ?>"
class="<?php echo esc_attr( $rx_section_class ); ?>"
aria-labelledby="<?php echo esc_attr( $rx_section_id ); ?>-title"
>
<div class="rx-related-posts__inner">
<header class="rx-related-posts__header">
<?php if ( ! empty( $rx_related_settings['title'] ) ) : ?>
<h2 id="<?php echo esc_attr( $rx_section_id ); ?>-title" class="rx-related-posts__title">
<?php echo esc_html( $rx_related_settings['title'] ); ?>
</h2>
<?php endif; ?>
<?php if ( ! empty( $rx_related_settings['subtitle'] ) ) : ?>
<p class="rx-related-posts__subtitle">
<?php echo esc_html( $rx_related_settings['subtitle'] ); ?>
</p>
<?php endif; ?>
</header>
<div class="rx-related-posts__grid">
<?php foreach ( $rx_related_posts as $rx_related_post_id ) : ?>
<?php
$rx_related_post_id = absint( $rx_related_post_id );
if ( ! $rx_related_post_id ) {
continue;
}
$rx_title = get_the_title( $rx_related_post_id );
$rx_permalink = get_permalink( $rx_related_post_id );
$rx_category = rx_related_posts_primary_category( $rx_related_post_id );
$rx_excerpt = rx_related_posts_trim_excerpt(
$rx_related_post_id,
absint( $rx_related_settings['excerpt_length'] )
);
$rx_author_id = absint( get_post_field( 'post_author', $rx_related_post_id ) );
?>
<article
class="rx-related-posts__card"
itemscope
itemtype="https://schema.org/BlogPosting"
>
<?php if ( ! empty( $rx_related_settings['show_thumbnail'] ) ) : ?>
<a
class="rx-related-posts__media"
href="<?php echo esc_url( $rx_permalink ); ?>"
aria-label="<?php echo esc_attr( sprintf( __( 'Read article: %s', 'rx-theme' ), $rx_title ) ); ?>"
<?php echo $rx_target; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php echo $rx_rel; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
>
<?php
echo rx_related_posts_thumbnail(
$rx_related_post_id,
$rx_related_settings['thumbnail_size'],
$rx_related_settings['default_thumbnail_url']
); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
?>
<?php if ( ! empty( $rx_related_settings['show_badge'] ) && ! empty( $rx_category ) ) : ?>
<span class="rx-related-posts__badge">
<?php echo esc_html( $rx_category->name ); ?>
</span>
<?php endif; ?>
</a>
<?php endif; ?>
<div class="rx-related-posts__content">
<?php if ( ! empty( $rx_related_settings['show_category'] ) && ! empty( $rx_category ) ) : ?>
<div class="rx-related-posts__category">
<a href="<?php echo esc_url( get_category_link( $rx_category->term_id ) ); ?>">
<?php echo esc_html( $rx_category->name ); ?>
</a>
</div>
<?php endif; ?>
<h3 class="rx-related-posts__post-title" itemprop="headline">
<a
href="<?php echo esc_url( $rx_permalink ); ?>"
itemprop="url"
<?php echo $rx_target; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php echo $rx_rel; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
>
<?php echo esc_html( $rx_title ); ?>
</a>
</h3>
<div class="rx-related-posts__meta">
<?php if ( ! empty( $rx_related_settings['show_author'] ) && $rx_author_id ) : ?>
<span class="rx-related-posts__meta-item rx-related-posts__meta-author" itemprop="author" itemscope itemtype="https://schema.org/Person">
<span class="rx-related-posts__meta-label">
<?php esc_html_e( 'By', 'rx-theme' ); ?>
</span>
<a href="<?php echo esc_url( get_author_posts_url( $rx_author_id ) ); ?>" itemprop="url">
<span itemprop="name">
<?php echo esc_html( get_the_author_meta( 'display_name', $rx_author_id ) ); ?>
</span>
</a>
</span>
<?php endif; ?>
<?php if ( ! empty( $rx_related_settings['show_date'] ) ) : ?>
<span class="rx-related-posts__meta-item rx-related-posts__meta-date">
<time
datetime="<?php echo esc_attr( get_the_date( DATE_W3C, $rx_related_post_id ) ); ?>"
itemprop="datePublished"
>
<?php echo esc_html( get_the_date( '', $rx_related_post_id ) ); ?>
</time>
</span>
<?php endif; ?>
<?php if ( ! empty( $rx_related_settings['show_reading_time'] ) ) : ?>
<span class="rx-related-posts__meta-item rx-related-posts__meta-reading-time">
<?php echo esc_html( rx_related_posts_reading_time( $rx_related_post_id ) ); ?>
</span>
<?php endif; ?>
<?php if ( ! empty( $rx_related_settings['show_comments'] ) ) : ?>
<span class="rx-related-posts__meta-item rx-related-posts__meta-comments">
<?php
printf(
esc_html(
_n(
'%s comment',
'%s comments',
get_comments_number( $rx_related_post_id ),
'rx-theme'
)
),
esc_html( number_format_i18n( get_comments_number( $rx_related_post_id ) ) )
);
?>
</span>
<?php endif; ?>
</div>
<?php if ( ! empty( $rx_related_settings['show_excerpt'] ) && ! empty( $rx_excerpt ) ) : ?>
<p class="rx-related-posts__excerpt" itemprop="description">
<?php echo esc_html( $rx_excerpt ); ?>
</p>
<?php endif; ?>
<a
class="rx-related-posts__read-more"
href="<?php echo esc_url( $rx_permalink ); ?>"
aria-label="<?php echo esc_attr( sprintf( __( 'Continue reading %s', 'rx-theme' ), $rx_title ) ); ?>"
<?php echo $rx_target; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php echo $rx_rel; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
>
<?php esc_html_e( 'Read More', 'rx-theme' ); ?>
<span aria-hidden="true">→</span>
</a>
</div>
<meta itemprop="mainEntityOfPage" content="<?php echo esc_url( $rx_permalink ); ?>">
<meta itemprop="dateModified" content="<?php echo esc_attr( get_the_modified_date( DATE_W3C, $rx_related_post_id ) ); ?>">
</article>
<?php endforeach; ?>
</div>
</div>
<?php if ( ! empty( $rx_related_settings['show_schema'] ) && ! empty( $rx_schema_items ) ) : ?>
<script type="application/ld+json">
<?php
echo wp_json_encode(
array(
'@context' => 'https://schema.org',
'@type' => 'ItemList',
'name' => wp_strip_all_tags( $rx_related_settings['title'] ),
'itemListElement' => $rx_schema_items,
),
JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT
);
?>
</script>
<?php endif; ?>
</section>
Add this in single.php
Place it after post content, before comments:
<?php
get_template_part( 'template-parts/post/related-posts' );
?>
Optional CSS for style.css
.rx-related-posts {
margin-top: 48px;
padding-top: 36px;
border-top: 1px solid rgba(0, 0, 0, 0.08);
}
.rx-related-posts__header {
margin-bottom: 24px;
text-align: center;
}
.rx-related-posts__title {
margin: 0 0 8px;
font-size: clamp(1.5rem, 2vw, 2rem);
line-height: 1.25;
}
.rx-related-posts__subtitle {
margin: 0 auto;
max-width: 680px;
color: #666;
font-size: 1rem;
line-height: 1.7;
}
.rx-related-posts__grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 24px;
}
.rx-related-posts--columns-1 .rx-related-posts__grid {
grid-template-columns: 1fr;
}
.rx-related-posts--columns-2 .rx-related-posts__grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.rx-related-posts--columns-3 .rx-related-posts__grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.rx-related-posts--columns-4 .rx-related-posts__grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.rx-related-posts__card {
overflow: hidden;
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 18px;
background: #fff;
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.06);
transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.rx-related-posts__card:hover {
transform: translateY(-4px);
box-shadow: 0 14px 36px rgba(0, 0, 0, 0.1);
}
.rx-related-posts__media {
position: relative;
display: block;
overflow: hidden;
aspect-ratio: 16 / 9;
background: #f4f4f4;
text-decoration: none;
}
.rx-related-posts__image {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
transition: transform 0.3s ease;
}
.rx-related-posts__card:hover .rx-related-posts__image {
transform: scale(1.05);
}
.rx-related-posts__image-placeholder {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #f3f4f6, #e5e7eb);
}
.rx-related-posts__image-placeholder-icon {
font-weight: 800;
font-size: 2rem;
letter-spacing: 0.08em;
color: #777;
}
.rx-related-posts__badge {
position: absolute;
left: 14px;
top: 14px;
z-index: 2;
padding: 6px 10px;
border-radius: 999px;
background: rgba(0, 0, 0, 0.72);
color: #fff;
font-size: 0.75rem;
font-weight: 700;
line-height: 1;
}
.rx-related-posts__content {
padding: 18px;
}
.rx-related-posts__category {
margin-bottom: 8px;
font-size: 0.78rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.rx-related-posts__category a {
text-decoration: none;
}
.rx-related-posts__post-title {
margin: 0 0 10px;
font-size: 1.1rem;
line-height: 1.35;
}
.rx-related-posts__post-title a {
color: inherit;
text-decoration: none;
}
.rx-related-posts__post-title a:hover {
text-decoration: underline;
}
.rx-related-posts__meta {
display: flex;
flex-wrap: wrap;
gap: 8px 12px;
margin-bottom: 12px;
color: #666;
font-size: 0.82rem;
line-height: 1.5;
}
.rx-related-posts__meta a {
color: inherit;
text-decoration: none;
}
.rx-related-posts__meta a:hover {
text-decoration: underline;
}
.rx-related-posts__excerpt {
margin: 0 0 14px;
color: #555;
font-size: 0.95rem;
line-height: 1.7;
}
.rx-related-posts__read-more {
display: inline-flex;
align-items: center;
gap: 6px;
font-weight: 700;
text-decoration: none;
}
.rx-related-posts__read-more:hover {
text-decoration: underline;
}
@media (max-width: 1024px) {
.rx-related-posts__grid,
.rx-related-posts--columns-3 .rx-related-posts__grid,
.rx-related-posts--columns-4 .rx-related-posts__grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 640px) {
.rx-related-posts {
margin-top: 36px;
padding-top: 28px;
}
.rx-related-posts__grid,
.rx-related-posts--columns-2 .rx-related-posts__grid,
.rx-related-posts--columns-3 .rx-related-posts__grid,
.rx-related-posts--columns-4 .rx-related-posts__grid {
grid-template-columns: 1fr;
}
}
Optional: control it from functions.php
Add this if you want to customize the related posts globally:
add_filter( 'rx_related_posts_settings', function( $settings ) {
$settings['title'] = 'Related Medical Articles';
$settings['subtitle'] = 'More useful articles selected from similar topics.';
$settings['posts_per_page'] = 6;
$settings['columns'] = 3;
$settings['show_thumbnail'] = true;
$settings['show_excerpt'] = true;
$settings['show_author'] = true;
$settings['show_date'] = true;
$settings['show_comments'] = false;
$settings['show_reading_time'] = true;
$settings['cache_enabled'] = true;
$settings['cache_expiration'] = 12 * HOUR_IN_SECONDS;
$settings['popular_fallback'] = true;
$settings['random_fallback'] = false;
$settings['default_thumbnail_url'] = get_template_directory_uri() . '/assets/images/default-related.jpg';
return $settings;
} );
Optional: include custom taxonomies
If your RX Theme later has medical custom taxonomies, add:
add_filter( 'rx_related_posts_custom_taxonomies', function( $taxonomies ) {
$taxonomies[] = 'disease_category';
$taxonomies[] = 'medical_specialty';
$taxonomies[] = 'rx_condition';
$taxonomies[] = 'topic';
return $taxonomies;
} );
This file is safe for a professional WordPress theme because it uses escaping, avoids heavy pagination queries with no_found_rows, supports caching, excludes the current post, and resets post data after custom queries.