I’ll build this as a reusable WooCommerce product layout module for your RX theme, focused on hooks, layout controls, badges, sale countdown, gallery tweaks, trust blocks, tabs, upsells, related products, schema-friendly markup, and safe fallbacks.
Below is an advanced inc/woocommerce/product-layout.php file for your RX theme. It adds many possible WooCommerce single-product layout features safely, using theme options through filters so you can customize later.
Create this file:
rx-theme/inc/woocommerce/product-layout.php
Then include it from your functions.php:
if ( file_exists( get_template_directory() . '/inc/woocommerce/product-layout.php' ) ) {
require_once get_template_directory() . '/inc/woocommerce/product-layout.php';
}
inc/woocommerce/product-layout.php
<?php
/**
* RX Theme WooCommerce Product Layout
*
* Advanced single product layout, product badges, gallery tweaks,
* trust box, delivery info, sticky add-to-cart, custom tabs,
* related/upsell/cross-sell controls, sale countdown, product meta,
* schema-friendly sections, and safe hook management.
*
* @package RX_Theme
*/
defined( 'ABSPATH' ) || exit;
if ( ! class_exists( 'WooCommerce' ) ) {
return;
}
if ( ! class_exists( 'RX_WooCommerce_Product_Layout' ) ) :
final class RX_WooCommerce_Product_Layout {
/**
* Class instance.
*
* @var RX_WooCommerce_Product_Layout|null
*/
private static $instance = null;
/**
* Get instance.
*/
public static function instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor.
*/
private function __construct() {
add_action( 'after_setup_theme', array( $this, 'theme_support' ), 20 );
add_action( 'wp', array( $this, 'setup_single_product_hooks' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ), 30 );
add_filter( 'body_class', array( $this, 'body_classes' ) );
add_filter( 'woocommerce_product_tabs', array( $this, 'custom_product_tabs' ), 30 );
add_filter( 'woocommerce_output_related_products_args', array( $this, 'related_products_args' ) );
add_filter( 'woocommerce_upsell_display_args', array( $this, 'upsell_products_args' ) );
add_filter( 'woocommerce_sale_flash', array( $this, 'custom_sale_flash' ), 20, 3 );
add_filter( 'woocommerce_get_price_html', array( $this, 'price_html_enhancement' ), 20, 2 );
add_filter( 'woocommerce_single_product_image_gallery_classes', array( $this, 'gallery_classes' ) );
add_action( 'woocommerce_before_shop_loop_item_title', array( $this, 'loop_product_badges' ), 9 );
add_action( 'woocommerce_before_single_product_summary', array( $this, 'single_product_badges' ), 5 );
add_action( 'woocommerce_share', array( $this, 'share_buttons' ), 20 );
add_action( 'wp_footer', array( $this, 'sticky_add_to_cart' ) );
add_action( 'wp_footer', array( $this, 'inline_product_script' ), 99 );
}
/**
* Default option helper.
*/
private function option( $key, $default = null ) {
$options = array(
'layout_style' => 'default',
'enable_breadcrumb' => true,
'enable_badges' => true,
'enable_sale_countdown' => true,
'enable_stock_notice' => true,
'enable_short_info' => true,
'enable_trust_box' => true,
'enable_delivery_box' => true,
'enable_secure_checkout' => true,
'enable_product_policy' => true,
'enable_share_buttons' => true,
'enable_sticky_cart' => true,
'enable_sku_highlight' => true,
'enable_brand_meta' => true,
'enable_category_chips' => true,
'enable_tag_chips' => true,
'enable_custom_tabs' => true,
'enable_size_guide_tab' => true,
'enable_shipping_tab' => true,
'enable_faq_tab' => true,
'enable_after_summary_banner' => true,
'enable_related_products' => true,
'enable_upsells' => true,
'related_products_count' => 4,
'related_products_columns' => 4,
'upsell_products_count' => 4,
'upsell_products_columns' => 4,
'show_free_shipping_threshold' => true,
'free_shipping_threshold' => 100,
'currency_symbol' => get_woocommerce_currency_symbol(),
'low_stock_quantity' => 5,
);
$options = apply_filters( 'rx_product_layout_options', $options );
return isset( $options[ $key ] ) ? $options[ $key ] : $default;
}
/**
* WooCommerce gallery support.
*/
public function theme_support() {
add_theme_support( 'woocommerce' );
add_theme_support( 'wc-product-gallery-zoom' );
add_theme_support( 'wc-product-gallery-lightbox' );
add_theme_support( 'wc-product-gallery-slider' );
}
/**
* Setup hooks for single product page.
*/
public function setup_single_product_hooks() {
if ( ! is_product() ) {
return;
}
if ( ! $this->option( 'enable_breadcrumb', true ) ) {
remove_action( 'woocommerce_before_main_content', 'woocommerce_breadcrumb', 20 );
}
add_action( 'woocommerce_before_single_product', array( $this, 'opening_product_wrapper' ), 1 );
add_action( 'woocommerce_after_single_product', array( $this, 'closing_product_wrapper' ), 99 );
add_action( 'woocommerce_single_product_summary', array( $this, 'product_subtitle_area' ), 6 );
add_action( 'woocommerce_single_product_summary', array( $this, 'rating_summary_box' ), 11 );
add_action( 'woocommerce_single_product_summary', array( $this, 'stock_and_sold_notice' ), 21 );
add_action( 'woocommerce_single_product_summary', array( $this, 'sale_countdown' ), 22 );
add_action( 'woocommerce_single_product_summary', array( $this, 'short_info_list' ), 23 );
add_action( 'woocommerce_single_product_summary', array( $this, 'delivery_box' ), 31 );
add_action( 'woocommerce_single_product_summary', array( $this, 'trust_box' ), 32 );
add_action( 'woocommerce_single_product_summary', array( $this, 'secure_checkout_box' ), 33 );
add_action( 'woocommerce_single_product_summary', array( $this, 'enhanced_product_meta' ), 41 );
add_action( 'woocommerce_after_single_product_summary', array( $this, 'after_summary_banner' ), 7 );
add_action( 'woocommerce_after_single_product_summary', array( $this, 'product_policy_grid' ), 8 );
if ( ! $this->option( 'enable_related_products', true ) ) {
remove_action( 'woocommerce_after_single_product_summary', 'woocommerce_output_related_products', 20 );
}
if ( ! $this->option( 'enable_upsells', true ) ) {
remove_action( 'woocommerce_after_single_product_summary', 'woocommerce_upsell_display', 15 );
}
}
/**
* Body classes.
*/
public function body_classes( $classes ) {
if ( is_product() ) {
$classes[] = 'rx-single-product';
$classes[] = 'rx-product-layout-' . sanitize_html_class( $this->option( 'layout_style', 'default' ) );
if ( $this->option( 'enable_sticky_cart', true ) ) {
$classes[] = 'rx-has-sticky-cart';
}
}
return $classes;
}
/**
* Enqueue CSS/JS.
*/
public function enqueue_assets() {
if ( ! is_product() && ! is_shop() && ! is_product_taxonomy() ) {
return;
}
$css = $this->inline_css();
wp_register_style( 'rx-product-layout', false, array(), RX_THEME_VERSION ?? '1.0.0' );
wp_enqueue_style( 'rx-product-layout' );
wp_add_inline_style( 'rx-product-layout', $css );
wp_register_script( 'rx-product-layout', false, array( 'jquery' ), RX_THEME_VERSION ?? '1.0.0', true );
wp_enqueue_script( 'rx-product-layout' );
}
/**
* Opening wrapper.
*/
public function opening_product_wrapper() {
echo '<div class="rx-product-page-shell">';
}
/**
* Closing wrapper.
*/
public function closing_product_wrapper() {
echo '</div>';
}
/**
* Product subtitle.
*/
public function product_subtitle_area() {
global $product;
if ( ! $product instanceof WC_Product ) {
return;
}
$subtitle = get_post_meta( $product->get_id(), '_rx_product_subtitle', true );
if ( empty( $subtitle ) ) {
$subtitle = apply_filters( 'rx_product_default_subtitle', '', $product );
}
if ( ! empty( $subtitle ) ) {
echo '<div class="rx-product-subtitle">' . esc_html( $subtitle ) . '</div>';
}
}
/**
* Rating summary.
*/
public function rating_summary_box() {
global $product;
if ( ! $product instanceof WC_Product ) {
return;
}
$count = $product->get_rating_count();
$rating = $product->get_average_rating();
if ( $count <= 0 ) {
echo '<div class="rx-rating-summary rx-no-rating">';
echo '<span>' . esc_html__( 'No reviews yet', 'rx-theme' ) . '</span>';
echo '</div>';
return;
}
echo '<div class="rx-rating-summary">';
echo '<span class="rx-rating-score">' . esc_html( number_format_i18n( $rating, 1 ) ) . '</span>';
echo wc_get_rating_html( $rating, $count );
echo '<a href="#reviews" class="rx-review-link">';
printf(
esc_html( _n( '%s customer review', '%s customer reviews', $count, 'rx-theme' ) ),
esc_html( number_format_i18n( $count ) )
);
echo '</a>';
echo '</div>';
}
/**
* Stock and sold notice.
*/
public function stock_and_sold_notice() {
if ( ! $this->option( 'enable_stock_notice', true ) ) {
return;
}
global $product;
if ( ! $product instanceof WC_Product ) {
return;
}
echo '<div class="rx-stock-sold-box">';
if ( $product->is_in_stock() ) {
$stock_quantity = $product->get_stock_quantity();
if ( null !== $stock_quantity && $stock_quantity > 0 && $stock_quantity <= absint( $this->option( 'low_stock_quantity', 5 ) ) ) {
echo '<div class="rx-low-stock">' . esc_html( sprintf( __( 'Only %d item(s) left in stock.', 'rx-theme' ), $stock_quantity ) ) . '</div>';
} else {
echo '<div class="rx-in-stock">' . esc_html__( 'Available in stock', 'rx-theme' ) . '</div>';
}
} else {
echo '<div class="rx-out-stock">' . esc_html__( 'Currently out of stock', 'rx-theme' ) . '</div>';
}
$total_sales = (int) $product->get_total_sales();
if ( $total_sales > 0 ) {
echo '<div class="rx-sold-count">';
echo esc_html( sprintf( __( '%d sold recently', 'rx-theme' ), $total_sales ) );
echo '</div>';
}
echo '</div>';
}
/**
* Sale countdown.
*/
public function sale_countdown() {
if ( ! $this->option( 'enable_sale_countdown', true ) ) {
return;
}
global $product;
if ( ! $product instanceof WC_Product || ! $product->is_on_sale() ) {
return;
}
$date_to = $product->get_date_on_sale_to();
if ( ! $date_to ) {
return;
}
$timestamp = $date_to->getTimestamp();
if ( $timestamp <= time() ) {
return;
}
echo '<div class="rx-sale-countdown" data-rx-countdown="' . esc_attr( $timestamp ) . '">';
echo '<span class="rx-sale-countdown-label">' . esc_html__( 'Sale ends in:', 'rx-theme' ) . '</span>';
echo '<span class="rx-countdown-value">';
echo '<span data-days>00</span>d ';
echo '<span data-hours>00</span>h ';
echo '<span data-minutes>00</span>m ';
echo '<span data-seconds>00</span>s';
echo '</span>';
echo '</div>';
}
/**
* Short product info.
*/
public function short_info_list() {
if ( ! $this->option( 'enable_short_info', true ) ) {
return;
}
global $product;
if ( ! $product instanceof WC_Product ) {
return;
}
$items = array();
if ( $product->is_virtual() ) {
$items[] = __( 'Digital / virtual product', 'rx-theme' );
}
if ( $product->is_downloadable() ) {
$items[] = __( 'Downloadable product', 'rx-theme' );
}
if ( $product->get_sku() ) {
$items[] = sprintf( __( 'SKU: %s', 'rx-theme' ), $product->get_sku() );
}
if ( $product->get_weight() ) {
$items[] = sprintf( __( 'Weight: %s', 'rx-theme' ), wc_format_weight( $product->get_weight() ) );
}
$items = apply_filters( 'rx_product_short_info_items', $items, $product );
if ( empty( $items ) ) {
return;
}
echo '<ul class="rx-product-short-info">';
foreach ( $items as $item ) {
echo '<li>' . esc_html( $item ) . '</li>';
}
echo '</ul>';
}
/**
* Delivery box.
*/
public function delivery_box() {
if ( ! $this->option( 'enable_delivery_box', true ) ) {
return;
}
$threshold = floatval( $this->option( 'free_shipping_threshold', 100 ) );
$symbol = $this->option( 'currency_symbol', get_woocommerce_currency_symbol() );
echo '<div class="rx-delivery-box">';
echo '<div class="rx-box-title">' . esc_html__( 'Delivery Information', 'rx-theme' ) . '</div>';
echo '<ul>';
echo '<li>' . esc_html__( 'Fast order processing after confirmation.', 'rx-theme' ) . '</li>';
echo '<li>' . esc_html__( 'Secure packaging for safer delivery.', 'rx-theme' ) . '</li>';
if ( $this->option( 'show_free_shipping_threshold', true ) ) {
echo '<li>' . esc_html( sprintf( __( 'Free shipping may be available over %1$s%2$s.', 'rx-theme' ), $symbol, $threshold ) ) . '</li>';
}
echo '</ul>';
echo '</div>';
}
/**
* Trust box.
*/
public function trust_box() {
if ( ! $this->option( 'enable_trust_box', true ) ) {
return;
}
$items = apply_filters(
'rx_product_trust_items',
array(
array(
'title' => __( 'Quality Checked', 'rx-theme' ),
'text' => __( 'Product information is reviewed before publishing.', 'rx-theme' ),
),
array(
'title' => __( 'Secure Shopping', 'rx-theme' ),
'text' => __( 'Your checkout process is protected.', 'rx-theme' ),
),
array(
'title' => __( 'Support Available', 'rx-theme' ),
'text' => __( 'Contact support for order-related help.', 'rx-theme' ),
),
)
);
echo '<div class="rx-trust-box">';
foreach ( $items as $item ) {
$title = isset( $item['title'] ) ? $item['title'] : '';
$text = isset( $item['text'] ) ? $item['text'] : '';
echo '<div class="rx-trust-item">';
echo '<strong>' . esc_html( $title ) . '</strong>';
echo '<span>' . esc_html( $text ) . '</span>';
echo '</div>';
}
echo '</div>';
}
/**
* Secure checkout box.
*/
public function secure_checkout_box() {
if ( ! $this->option( 'enable_secure_checkout', true ) ) {
return;
}
echo '<div class="rx-secure-checkout">';
echo '<strong>' . esc_html__( 'Secure checkout', 'rx-theme' ) . '</strong>';
echo '<span>' . esc_html__( 'Payments and order details are handled through WooCommerce checkout.', 'rx-theme' ) . '</span>';
echo '</div>';
}
/**
* Enhanced product meta.
*/
public function enhanced_product_meta() {
global $product;
if ( ! $product instanceof WC_Product ) {
return;
}
echo '<div class="rx-enhanced-product-meta">';
if ( $this->option( 'enable_sku_highlight', true ) && $product->get_sku() ) {
echo '<div class="rx-meta-row rx-sku-row">';
echo '<span>' . esc_html__( 'Product Code:', 'rx-theme' ) . '</span>';
echo '<strong>' . esc_html( $product->get_sku() ) . '</strong>';
echo '</div>';
}
if ( $this->option( 'enable_category_chips', true ) ) {
$categories = wc_get_product_category_list( $product->get_id(), ' ' );
if ( $categories ) {
echo '<div class="rx-meta-row rx-category-row">';
echo '<span>' . esc_html__( 'Categories:', 'rx-theme' ) . '</span>';
echo '<div class="rx-meta-chips">' . wp_kses_post( $categories ) . '</div>';
echo '</div>';
}
}
if ( $this->option( 'enable_tag_chips', true ) ) {
$tags = wc_get_product_tag_list( $product->get_id(), ' ' );
if ( $tags ) {
echo '<div class="rx-meta-row rx-tag-row">';
echo '<span>' . esc_html__( 'Tags:', 'rx-theme' ) . '</span>';
echo '<div class="rx-meta-chips">' . wp_kses_post( $tags ) . '</div>';
echo '</div>';
}
}
do_action( 'rx_after_enhanced_product_meta', $product );
echo '</div>';
}
/**
* After summary banner.
*/
public function after_summary_banner() {
if ( ! $this->option( 'enable_after_summary_banner', true ) ) {
return;
}
echo '<section class="rx-after-summary-banner">';
echo '<div>';
echo '<h2>' . esc_html__( 'Why choose this product?', 'rx-theme' ) . '</h2>';
echo '<p>' . esc_html__( 'This section can highlight quality, delivery, warranty, return policy, or special product benefits.', 'rx-theme' ) . '</p>';
echo '</div>';
echo '</section>';
}
/**
* Policy grid.
*/
public function product_policy_grid() {
if ( ! $this->option( 'enable_product_policy', true ) ) {
return;
}
$policies = apply_filters(
'rx_product_policy_items',
array(
array(
'title' => __( 'Easy Returns', 'rx-theme' ),
'text' => __( 'Return policy depends on product type and store rules.', 'rx-theme' ),
),
array(
'title' => __( 'Original Product', 'rx-theme' ),
'text' => __( 'Product details are displayed transparently.', 'rx-theme' ),
),
array(
'title' => __( 'Safe Packaging', 'rx-theme' ),
'text' => __( 'Products are packed carefully before shipping.', 'rx-theme' ),
),
array(
'title' => __( 'Customer Help', 'rx-theme' ),
'text' => __( 'Support is available for order questions.', 'rx-theme' ),
),
)
);
echo '<section class="rx-product-policy-grid">';
foreach ( $policies as $policy ) {
$title = isset( $policy['title'] ) ? $policy['title'] : '';
$text = isset( $policy['text'] ) ? $policy['text'] : '';
echo '<article class="rx-policy-card">';
echo '<h3>' . esc_html( $title ) . '</h3>';
echo '<p>' . esc_html( $text ) . '</p>';
echo '</article>';
}
echo '</section>';
}
/**
* Product tabs.
*/
public function custom_product_tabs( $tabs ) {
if ( ! $this->option( 'enable_custom_tabs', true ) ) {
return $tabs;
}
if ( $this->option( 'enable_size_guide_tab', true ) ) {
$tabs['rx_size_guide'] = array(
'title' => __( 'Size Guide', 'rx-theme' ),
'priority' => 35,
'callback' => array( $this, 'size_guide_tab_content' ),
);
}
if ( $this->option( 'enable_shipping_tab', true ) ) {
$tabs['rx_shipping_info'] = array(
'title' => __( 'Shipping Info', 'rx-theme' ),
'priority' => 45,
'callback' => array( $this, 'shipping_tab_content' ),
);
}
if ( $this->option( 'enable_faq_tab', true ) ) {
$tabs['rx_product_faq'] = array(
'title' => __( 'FAQs', 'rx-theme' ),
'priority' => 55,
'callback' => array( $this, 'faq_tab_content' ),
);
}
return $tabs;
}
/**
* Size guide content.
*/
public function size_guide_tab_content() {
echo '<div class="rx-tab-content rx-size-guide-tab">';
echo '<h2>' . esc_html__( 'Size Guide', 'rx-theme' ) . '</h2>';
echo '<p>' . esc_html__( 'Use this section to add product measurement guidance. You can customize this content using the rx_size_guide_tab_content action.', 'rx-theme' ) . '</p>';
do_action( 'rx_size_guide_tab_content' );
echo '</div>';
}
/**
* Shipping tab content.
*/
public function shipping_tab_content() {
echo '<div class="rx-tab-content rx-shipping-tab">';
echo '<h2>' . esc_html__( 'Shipping Information', 'rx-theme' ) . '</h2>';
echo '<p>' . esc_html__( 'Shipping time and cost may depend on customer location, product type, and available delivery method.', 'rx-theme' ) . '</p>';
do_action( 'rx_shipping_tab_content' );
echo '</div>';
}
/**
* FAQ tab content.
*/
public function faq_tab_content() {
$faqs = apply_filters(
'rx_product_faq_items',
array(
array(
'q' => __( 'Is this product available now?', 'rx-theme' ),
'a' => __( 'Availability is shown near the product price and add-to-cart button.', 'rx-theme' ),
),
array(
'q' => __( 'Can I return this product?', 'rx-theme' ),
'a' => __( 'Return availability depends on the store policy and product condition.', 'rx-theme' ),
),
array(
'q' => __( 'How can I get support?', 'rx-theme' ),
'a' => __( 'You can contact the store support team through the contact page or order support channel.', 'rx-theme' ),
),
)
);
echo '<div class="rx-tab-content rx-faq-tab">';
echo '<h2>' . esc_html__( 'Frequently Asked Questions', 'rx-theme' ) . '</h2>';
foreach ( $faqs as $faq ) {
$q = isset( $faq['q'] ) ? $faq['q'] : '';
$a = isset( $faq['a'] ) ? $faq['a'] : '';
echo '<details class="rx-faq-item">';
echo '<summary>' . esc_html( $q ) . '</summary>';
echo '<p>' . esc_html( $a ) . '</p>';
echo '</details>';
}
echo '</div>';
}
/**
* Related products args.
*/
public function related_products_args( $args ) {
$args['posts_per_page'] = absint( $this->option( 'related_products_count', 4 ) );
$args['columns'] = absint( $this->option( 'related_products_columns', 4 ) );
return $args;
}
/**
* Upsell products args.
*/
public function upsell_products_args( $args ) {
$args['posts_per_page'] = absint( $this->option( 'upsell_products_count', 4 ) );
$args['columns'] = absint( $this->option( 'upsell_products_columns', 4 ) );
return $args;
}
/**
* Sale flash.
*/
public function custom_sale_flash( $html, $post, $product ) {
if ( ! $this->option( 'enable_badges', true ) || ! $product instanceof WC_Product ) {
return $html;
}
$percentage = $this->get_sale_percentage( $product );
if ( $percentage > 0 ) {
return '<span class="onsale rx-sale-badge">' . esc_html( sprintf( __( '-%d%%', 'rx-theme' ), $percentage ) ) . '</span>';
}
return '<span class="onsale rx-sale-badge">' . esc_html__( 'Sale', 'rx-theme' ) . '</span>';
}
/**
* Price enhancement.
*/
public function price_html_enhancement( $price, $product ) {
if ( ! $product instanceof WC_Product ) {
return $price;
}
if ( $product->is_on_sale() ) {
$percentage = $this->get_sale_percentage( $product );
if ( $percentage > 0 ) {
$price .= '<span class="rx-price-saving">' . esc_html( sprintf( __( 'Save %d%%', 'rx-theme' ), $percentage ) ) . '</span>';
}
}
return $price;
}
/**
* Gallery classes.
*/
public function gallery_classes( $classes ) {
$classes[] = 'rx-product-gallery';
$classes[] = 'rx-product-gallery-enhanced';
return $classes;
}
/**
* Loop badges.
*/
public function loop_product_badges() {
global $product;
if ( ! $product instanceof WC_Product || ! $this->option( 'enable_badges', true ) ) {
return;
}
echo '<div class="rx-loop-badges">';
$this->render_badges( $product );
echo '</div>';
}
/**
* Single product badges.
*/
public function single_product_badges() {
global $product;
if ( ! $product instanceof WC_Product || ! $this->option( 'enable_badges', true ) ) {
return;
}
echo '<div class="rx-single-badges">';
$this->render_badges( $product );
echo '</div>';
}
/**
* Render product badges.
*/
private function render_badges( $product ) {
if ( $product->is_on_sale() ) {
$percentage = $this->get_sale_percentage( $product );
if ( $percentage > 0 ) {
echo '<span class="rx-badge rx-badge-sale">' . esc_html( sprintf( __( '-%d%%', 'rx-theme' ), $percentage ) ) . '</span>';
} else {
echo '<span class="rx-badge rx-badge-sale">' . esc_html__( 'Sale', 'rx-theme' ) . '</span>';
}
}
if ( ! $product->is_in_stock() ) {
echo '<span class="rx-badge rx-badge-stock">' . esc_html__( 'Out of stock', 'rx-theme' ) . '</span>';
}
if ( $product->is_featured() ) {
echo '<span class="rx-badge rx-badge-featured">' . esc_html__( 'Featured', 'rx-theme' ) . '</span>';
}
$date_created = $product->get_date_created();
if ( $date_created && $date_created->getTimestamp() > strtotime( '-30 days' ) ) {
echo '<span class="rx-badge rx-badge-new">' . esc_html__( 'New', 'rx-theme' ) . '</span>';
}
do_action( 'rx_product_extra_badges', $product );
}
/**
* Get sale percentage.
*/
private function get_sale_percentage( $product ) {
if ( ! $product instanceof WC_Product || ! $product->is_on_sale() ) {
return 0;
}
if ( $product->is_type( 'variable' ) ) {
$regular_prices = $product->get_variation_regular_price( 'max', true );
$sale_prices = $product->get_variation_sale_price( 'min', true );
if ( $regular_prices > 0 && $sale_prices > 0 ) {
return round( ( ( $regular_prices - $sale_prices ) / $regular_prices ) * 100 );
}
}
$regular = (float) $product->get_regular_price();
$sale = (float) $product->get_sale_price();
if ( $regular > 0 && $sale > 0 ) {
return round( ( ( $regular - $sale ) / $regular ) * 100 );
}
return 0;
}
/**
* Share buttons.
*/
public function share_buttons() {
if ( ! $this->option( 'enable_share_buttons', true ) ) {
return;
}
global $product;
if ( ! $product instanceof WC_Product ) {
return;
}
$url = rawurlencode( get_permalink( $product->get_id() ) );
$title = rawurlencode( get_the_title( $product->get_id() ) );
echo '<div class="rx-product-share">';
echo '<span>' . esc_html__( 'Share:', 'rx-theme' ) . '</span>';
echo '<a target="_blank" rel="noopener noreferrer" href="' . esc_url( 'https://www.facebook.com/sharer/sharer.php?u=' . $url ) . '">' . esc_html__( 'Facebook', 'rx-theme' ) . '</a>';
echo '<a target="_blank" rel="noopener noreferrer" href="' . esc_url( 'https://twitter.com/intent/tweet?url=' . $url . '&text=' . $title ) . '">' . esc_html__( 'X', 'rx-theme' ) . '</a>';
echo '<a target="_blank" rel="noopener noreferrer" href="' . esc_url( 'https://www.linkedin.com/shareArticle?mini=true&url=' . $url . '&title=' . $title ) . '">' . esc_html__( 'LinkedIn', 'rx-theme' ) . '</a>';
echo '<a target="_blank" rel="noopener noreferrer" href="' . esc_url( 'https://api.whatsapp.com/send?text=' . $title . '%20' . $url ) . '">' . esc_html__( 'WhatsApp', 'rx-theme' ) . '</a>';
echo '</div>';
}
/**
* Sticky add to cart.
*/
public function sticky_add_to_cart() {
if ( ! is_product() || ! $this->option( 'enable_sticky_cart', true ) ) {
return;
}
global $product;
if ( ! $product instanceof WC_Product || ! $product->is_purchasable() ) {
return;
}
echo '<div class="rx-sticky-add-to-cart" data-rx-sticky-cart>';
echo '<div class="rx-sticky-inner">';
echo '<div class="rx-sticky-product-info">';
if ( has_post_thumbnail( $product->get_id() ) ) {
echo get_the_post_thumbnail( $product->get_id(), 'thumbnail', array( 'class' => 'rx-sticky-thumb' ) );
}
echo '<div>';
echo '<strong>' . esc_html( get_the_title( $product->get_id() ) ) . '</strong>';
echo '<span>' . wp_kses_post( $product->get_price_html() ) . '</span>';
echo '</div>';
echo '</div>';
echo '<a class="button rx-sticky-cart-button" href="#product-' . esc_attr( $product->get_id() ) . '">';
echo esc_html__( 'View Purchase Options', 'rx-theme' );
echo '</a>';
echo '</div>';
echo '</div>';
}
/**
* Inline product JS.
*/
public function inline_product_script() {
if ( ! is_product() ) {
return;
}
?>
<script>
(function () {
'use strict';
function rxCountdown() {
var timers = document.querySelectorAll('[data-rx-countdown]');
if (!timers.length) {
return;
}
timers.forEach(function (timer) {
var end = parseInt(timer.getAttribute('data-rx-countdown'), 10) * 1000;
function tick() {
var now = new Date().getTime();
var distance = end - now;
if (distance <= 0) {
timer.style.display = 'none';
return;
}
var days = Math.floor(distance / (1000 * 60 * 60 * 24));
var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
var seconds = Math.floor((distance % (1000 * 60)) / 1000);
var d = timer.querySelector('[data-days]');
var h = timer.querySelector('[data-hours]');
var m = timer.querySelector('[data-minutes]');
var s = timer.querySelector('[data-seconds]');
if (d) d.textContent = String(days).padStart(2, '0');
if (h) h.textContent = String(hours).padStart(2, '0');
if (m) m.textContent = String(minutes).padStart(2, '0');
if (s) s.textContent = String(seconds).padStart(2, '0');
}
tick();
setInterval(tick, 1000);
});
}
function rxStickyCart() {
var sticky = document.querySelector('[data-rx-sticky-cart]');
var cart = document.querySelector('form.cart');
if (!sticky || !cart) {
return;
}
function toggleSticky() {
var rect = cart.getBoundingClientRect();
if (rect.bottom < 0) {
sticky.classList.add('is-visible');
} else {
sticky.classList.remove('is-visible');
}
}
window.addEventListener('scroll', toggleSticky, { passive: true });
window.addEventListener('resize', toggleSticky);
toggleSticky();
}
document.addEventListener('DOMContentLoaded', function () {
rxCountdown();
rxStickyCart();
});
})();
</script>
<?php
}
/**
* Inline CSS.
*/
private function inline_css() {
return '
.rx-product-page-shell {
position: relative;
}
.rx-single-product .summary {
position: relative;
}
.rx-product-subtitle {
margin: 6px 0 12px;
font-size: 15px;
color: #555;
line-height: 1.6;
}
.rx-rating-summary {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 10px 0 14px;
font-size: 14px;
}
.rx-rating-score {
font-weight: 700;
padding: 3px 8px;
border-radius: 999px;
background: rgba(0,0,0,0.06);
}
.rx-review-link {
text-decoration: none;
}
.rx-stock-sold-box {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin: 12px 0;
}
.rx-stock-sold-box > div {
padding: 7px 12px;
border-radius: 999px;
font-size: 13px;
font-weight: 600;
background: rgba(0,0,0,0.06);
}
.rx-in-stock {
color: #087f23;
}
.rx-low-stock {
color: #a15c00;
}
.rx-out-stock {
color: #b00020;
}
.rx-sale-countdown {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
margin: 14px 0;
padding: 12px 14px;
border-radius: 12px;
background: rgba(220, 53, 69, 0.08);
border: 1px solid rgba(220, 53, 69, 0.18);
}
.rx-sale-countdown-label {
font-weight: 700;
}
.rx-countdown-value span {
font-weight: 800;
}
.rx-product-short-info {
margin: 14px 0;
padding: 0;
list-style: none;
display: grid;
gap: 8px;
}
.rx-product-short-info li {
padding: 9px 12px;
border-radius: 10px;
background: rgba(0,0,0,0.04);
}
.rx-delivery-box,
.rx-secure-checkout,
.rx-trust-box {
margin: 16px 0;
padding: 16px;
border-radius: 14px;
border: 1px solid rgba(0,0,0,0.08);
background: #fff;
box-shadow: 0 8px 22px rgba(0,0,0,0.04);
}
.rx-box-title {
font-weight: 800;
margin-bottom: 8px;
}
.rx-delivery-box ul {
margin: 0;
padding-left: 18px;
}
.rx-trust-box {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
}
.rx-trust-item {
display: grid;
gap: 4px;
}
.rx-trust-item strong {
font-size: 14px;
}
.rx-trust-item span {
font-size: 13px;
color: #666;
line-height: 1.5;
}
.rx-secure-checkout {
display: grid;
gap: 4px;
}
.rx-secure-checkout strong {
font-size: 15px;
}
.rx-secure-checkout span {
font-size: 13px;
color: #666;
}
.rx-enhanced-product-meta {
margin: 18px 0;
display: grid;
gap: 10px;
}
.rx-meta-row {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
font-size: 14px;
}
.rx-meta-row > span {
font-weight: 700;
}
.rx-meta-chips {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.rx-meta-chips a {
display: inline-flex;
padding: 5px 9px;
border-radius: 999px;
background: rgba(0,0,0,0.06);
text-decoration: none;
font-size: 12px;
}
.rx-after-summary-banner {
margin: 28px 0;
padding: 26px;
border-radius: 18px;
background: linear-gradient(135deg, rgba(0,0,0,0.04), rgba(0,0,0,0.01));
border: 1px solid rgba(0,0,0,0.06);
}
.rx-after-summary-banner h2 {
margin-top: 0;
margin-bottom: 8px;
}
.rx-after-summary-banner p {
margin-bottom: 0;
}
.rx-product-policy-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 16px;
margin: 26px 0;
}
.rx-policy-card {
padding: 18px;
border-radius: 14px;
border: 1px solid rgba(0,0,0,0.08);
background: #fff;
}
.rx-policy-card h3 {
margin-top: 0;
margin-bottom: 8px;
font-size: 16px;
}
.rx-policy-card p {
margin: 0;
font-size: 14px;
color: #666;
line-height: 1.6;
}
.rx-single-badges,
.rx-loop-badges {
position: absolute;
z-index: 5;
top: 12px;
left: 12px;
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.rx-loop-badges {
top: 10px;
left: 10px;
}
.rx-badge,
.rx-sale-badge {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 5px 9px;
border-radius: 999px;
font-size: 12px;
font-weight: 800;
line-height: 1;
background: #111;
color: #fff;
}
.rx-badge-sale,
.rx-sale-badge {
background: #d92332;
}
.rx-badge-new {
background: #087f23;
}
.rx-badge-featured {
background: #5b35d5;
}
.rx-badge-stock {
background: #555;
}
.rx-price-saving {
display: inline-flex;
margin-left: 8px;
padding: 4px 8px;
border-radius: 999px;
font-size: 12px;
font-weight: 700;
background: rgba(217,35,50,0.08);
color: #d92332;
vertical-align: middle;
}
.rx-product-share {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
margin-top: 16px;
}
.rx-product-share span {
font-weight: 700;
}
.rx-product-share a {
display: inline-flex;
padding: 6px 10px;
border-radius: 999px;
background: rgba(0,0,0,0.06);
text-decoration: none;
font-size: 13px;
}
.rx-faq-item {
padding: 12px 0;
border-bottom: 1px solid rgba(0,0,0,0.08);
}
.rx-faq-item summary {
cursor: pointer;
font-weight: 700;
}
.rx-faq-item p {
margin-bottom: 0;
color: #666;
}
.rx-sticky-add-to-cart {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
padding: 10px 16px;
background: #fff;
border-top: 1px solid rgba(0,0,0,0.08);
box-shadow: 0 -8px 24px rgba(0,0,0,0.08);
transform: translateY(110%);
opacity: 0;
transition: transform .25s ease, opacity .25s ease;
}
.rx-sticky-add-to-cart.is-visible {
transform: translateY(0);
opacity: 1;
}
.rx-sticky-inner {
max-width: 1200px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
}
.rx-sticky-product-info {
display: flex;
align-items: center;
gap: 10px;
min-width: 0;
}
.rx-sticky-product-info strong {
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 420px;
}
.rx-sticky-product-info span {
display: block;
font-size: 13px;
}
.rx-sticky-thumb {
width: 46px;
height: 46px;
object-fit: cover;
border-radius: 8px;
}
.rx-sticky-cart-button {
white-space: nowrap;
}
@media (max-width: 900px) {
.rx-trust-box,
.rx-product-policy-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 600px) {
.rx-trust-box,
.rx-product-policy-grid {
grid-template-columns: 1fr;
}
.rx-sticky-product-info strong {
max-width: 180px;
}
.rx-sticky-inner {
align-items: stretch;
}
.rx-sticky-cart-button {
display: flex;
align-items: center;
justify-content: center;
}
}
';
}
}
endif;
RX_WooCommerce_Product_Layout::instance();
/**
* Extra helper: custom product subtitle meta save support.
* You can later connect this field with admin UI if needed.
*/
if ( ! function_exists( 'rx_get_product_subtitle' ) ) {
function rx_get_product_subtitle( $product_id = 0 ) {
$product_id = $product_id ? absint( $product_id ) : get_the_ID();
return get_post_meta( $product_id, '_rx_product_subtitle', true );
}
}
/**
* Extra helper: check if current page is RX product page.
*/
if ( ! function_exists( 'rx_is_single_product_page' ) ) {
function rx_is_single_product_page() {
return function_exists( 'is_product' ) && is_product();
}
}
/**
* Extra helper: product badge output anywhere.
*/
if ( ! function_exists( 'rx_product_badge_output' ) ) {
function rx_product_badge_output( $product_id = 0 ) {
$product_id = $product_id ? absint( $product_id ) : get_the_ID();
$product = wc_get_product( $product_id );
if ( ! $product ) {
return;
}
if ( $product->is_on_sale() ) {
echo '<span class="rx-badge rx-badge-sale">' . esc_html__( 'Sale', 'rx-theme' ) . '</span>';
}
if ( $product->is_featured() ) {
echo '<span class="rx-badge rx-badge-featured">' . esc_html__( 'Featured', 'rx-theme' ) . '</span>';
}
if ( ! $product->is_in_stock() ) {
echo '<span class="rx-badge rx-badge-stock">' . esc_html__( 'Out of stock', 'rx-theme' ) . '</span>';
}
}
}
/**
* Example filter customization.
*
* You can move this to functions.php if you want to change settings.
*/
/*
add_filter( 'rx_product_layout_options', function( $options ) {
$options['related_products_count'] = 8;
$options['related_products_columns'] = 4;
$options['upsell_products_count'] = 4;
$options['free_shipping_threshold'] = 150;
return $options;
} );
*/
/**
* Example custom trust items.
*/
/*
add_filter( 'rx_product_trust_items', function( $items ) {
return array(
array(
'title' => 'Doctor Reviewed',
'text' => 'Product information is checked carefully.',
),
array(
'title' => 'Secure Order',
'text' => 'Safe checkout with WooCommerce.',
),
array(
'title' => 'Fast Support',
'text' => 'Support available for order help.',
),
);
} );
*/
This file is safe to use as a strong starting product layout module. After adding it, clear your cache and check one WooCommerce product page. If any section looks too much, you can disable it through the rx_product_layout_options filter.