I’ll build this as a complete inc/integrations/elementor.php file for your RX theme: compatibility checks, theme support, widget areas, custom Elementor widgets/categories, editor styles/scripts, performance-safe frontend helpers, schema/accessibility tweaks, and safe fallbacks.
Below is an advanced inc/integrations/elementor.php file for your RX Theme. It adds Elementor compatibility, theme locations, editor/front-end assets, custom widget category, custom widgets, safe optimization, container defaults, SVG support note, accessibility helpers, body classes, template fallback helpers, and developer filters.
Elementor officially recommends registering widgets through the widgets manager, registering theme locations through elementor/theme/register_locations, and using Elementor-specific hooks for frontend/editor scripts and styles.
<?php
/**
* RX Theme - Elementor Integration
*
* File: inc/integrations/elementor.php
*
* @package RX_Theme
*/
defined( 'ABSPATH' ) || exit;
if ( ! class_exists( 'RX_Elementor_Integration' ) ) :
final class RX_Elementor_Integration {
/**
* Singleton instance.
*
* @var RX_Elementor_Integration|null
*/
private static $instance = null;
/**
* Theme version.
*
* @var string
*/
private $version;
/**
* Text domain.
*
* @var string
*/
private $textdomain = 'rx-theme';
/**
* Get instance.
*/
public static function instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor.
*/
private function __construct() {
$this->version = defined( 'RX_THEME_VERSION' ) ? RX_THEME_VERSION : wp_get_theme()->get( 'Version' );
$this->hooks();
}
/**
* Register hooks.
*/
private function hooks() {
/**
* Core compatibility.
*/
add_action( 'after_setup_theme', array( $this, 'add_theme_supports' ), 20 );
add_action( 'wp', array( $this, 'maybe_disable_theme_parts' ) );
/**
* Elementor availability.
*/
add_action( 'plugins_loaded', array( $this, 'maybe_boot_elementor_features' ), 20 );
/**
* Frontend helpers.
*/
add_filter( 'body_class', array( $this, 'body_classes' ) );
add_filter( 'post_class', array( $this, 'post_classes' ), 10, 3 );
/**
* Admin helper notice.
*/
add_action( 'admin_notices', array( $this, 'admin_notice_missing_elementor' ) );
/**
* Performance and markup.
*/
add_filter( 'elementor/frontend/print_google_fonts', array( $this, 'maybe_disable_elementor_google_fonts' ) );
add_filter( 'elementor/files/file_name', array( $this, 'elementor_css_file_name' ), 10, 2 );
}
/**
* Boot Elementor-specific hooks only if Elementor is loaded.
*/
public function maybe_boot_elementor_features() {
if ( ! did_action( 'elementor/loaded' ) ) {
return;
}
/**
* Elementor theme locations.
*/
add_action( 'elementor/theme/register_locations', array( $this, 'register_theme_locations' ) );
/**
* Elementor editor and frontend assets.
*/
add_action( 'elementor/frontend/before_register_scripts', array( $this, 'register_frontend_scripts' ) );
add_action( 'elementor/frontend/before_enqueue_styles', array( $this, 'register_frontend_styles' ) );
add_action( 'elementor/frontend/after_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) );
add_action( 'elementor/editor/before_enqueue_scripts', array( $this, 'enqueue_editor_scripts' ) );
add_action( 'elementor/editor/before_enqueue_styles', array( $this, 'enqueue_editor_styles' ) );
add_action( 'elementor/preview/enqueue_styles', array( $this, 'enqueue_preview_styles' ) );
/**
* Elementor widgets, categories and controls.
*/
add_action( 'elementor/elements/categories_registered', array( $this, 'register_widget_categories' ) );
add_action( 'elementor/widgets/register', array( $this, 'register_widgets' ) );
/**
* Elementor document/page settings.
*/
add_action( 'elementor/documents/register_controls', array( $this, 'register_document_controls' ) );
/**
* Elementor section/container/widget advanced controls.
*/
add_action( 'elementor/element/container/section_layout/after_section_end', array( $this, 'register_container_controls' ), 10, 2 );
add_action( 'elementor/element/section/section_layout/after_section_end', array( $this, 'register_section_controls' ), 10, 2 );
add_action( 'elementor/element/common/_section_style/after_section_end', array( $this, 'register_common_controls' ), 10, 2 );
/**
* Render filters.
*/
add_filter( 'elementor/widget/render_content', array( $this, 'filter_widget_render_content' ), 10, 2 );
add_action( 'elementor/frontend/before_render', array( $this, 'before_element_render' ) );
add_action( 'elementor/frontend/after_render', array( $this, 'after_element_render' ) );
/**
* Custom queries for Posts widget / Loop Grid.
*/
add_action( 'elementor/query/rx_related_posts', array( $this, 'query_related_posts' ) );
add_action( 'elementor/query/rx_latest_medical_posts', array( $this, 'query_latest_medical_posts' ) );
add_action( 'elementor/query/rx_popular_posts', array( $this, 'query_popular_posts' ) );
}
/**
* Add Elementor-compatible theme supports.
*/
public function add_theme_supports() {
add_theme_support( 'title-tag' );
add_theme_support( 'post-thumbnails' );
add_theme_support( 'automatic-feed-links' );
add_theme_support( 'responsive-embeds' );
add_theme_support( 'align-wide' );
add_theme_support( 'editor-styles' );
add_theme_support( 'wp-block-styles' );
add_theme_support( 'html5', array(
'search-form',
'comment-form',
'comment-list',
'gallery',
'caption',
'script',
'style',
'navigation-widgets',
) );
/**
* Elementor theme support.
*/
add_theme_support( 'elementor' );
/**
* Elementor Pro header/footer location support.
*/
add_theme_support( 'elementor-pro-theme-builder' );
/**
* WooCommerce + Elementor compatibility.
*/
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' );
}
/**
* Register Elementor Pro theme builder locations.
*
* Elementor supports registering all core locations or selected locations.
* Official hook: elementor/theme/register_locations.
*/
public function register_theme_locations( $elementor_theme_manager ) {
if ( ! $elementor_theme_manager ) {
return;
}
$register_all = apply_filters( 'rx_theme_elementor_register_all_core_locations', true );
if ( $register_all && method_exists( $elementor_theme_manager, 'register_all_core_location' ) ) {
$elementor_theme_manager->register_all_core_location();
}
$custom_locations = array(
'rx_top_bar' => array(
'label' => esc_html__( 'RX Top Bar', 'rx-theme' ),
'multiple' => true,
'edit_in_content' => false,
),
'rx_before_header' => array(
'label' => esc_html__( 'RX Before Header', 'rx-theme' ),
'multiple' => true,
'edit_in_content' => false,
),
'rx_after_header' => array(
'label' => esc_html__( 'RX After Header', 'rx-theme' ),
'multiple' => true,
'edit_in_content' => false,
),
'rx_before_content' => array(
'label' => esc_html__( 'RX Before Content', 'rx-theme' ),
'multiple' => true,
'edit_in_content' => true,
),
'rx_after_content' => array(
'label' => esc_html__( 'RX After Content', 'rx-theme' ),
'multiple' => true,
'edit_in_content' => true,
),
'rx_sidebar' => array(
'label' => esc_html__( 'RX Sidebar', 'rx-theme' ),
'multiple' => true,
'edit_in_content' => false,
),
'rx_before_footer' => array(
'label' => esc_html__( 'RX Before Footer', 'rx-theme' ),
'multiple' => true,
'edit_in_content' => false,
),
'rx_after_footer' => array(
'label' => esc_html__( 'RX After Footer', 'rx-theme' ),
'multiple' => true,
'edit_in_content' => false,
),
);
$custom_locations = apply_filters( 'rx_theme_elementor_custom_locations', $custom_locations );
foreach ( $custom_locations as $location => $args ) {
if ( method_exists( $elementor_theme_manager, 'register_location' ) ) {
$elementor_theme_manager->register_location( $location, $args );
}
}
}
/**
* Register frontend scripts.
*
* Elementor recommends registering scripts/styles with its own hooks
* so assets load only where needed.
*/
public function register_frontend_scripts() {
$uri = get_template_directory_uri();
wp_register_script(
'rx-elementor-frontend',
$uri . '/assets/js/elementor/frontend.js',
array( 'jquery', 'elementor-frontend' ),
$this->version,
true
);
wp_localize_script(
'rx-elementor-frontend',
'rxElementor',
array(
'ajaxUrl' => esc_url_raw( admin_url( 'admin-ajax.php' ) ),
'homeUrl' => esc_url_raw( home_url( '/' ) ),
'themeUrl' => esc_url_raw( get_template_directory_uri() ),
'isRtl' => is_rtl(),
'isUserLogged' => is_user_logged_in(),
'nonce' => wp_create_nonce( 'rx_elementor_nonce' ),
'breakpoints' => array(
'mobile' => 767,
'tablet' => 1024,
'desktop' => 1200,
),
)
);
}
/**
* Register/enqueue frontend styles.
*/
public function register_frontend_styles() {
$uri = get_template_directory_uri();
wp_register_style(
'rx-elementor-frontend',
$uri . '/assets/css/elementor/frontend.css',
array(),
$this->version
);
wp_enqueue_style( 'rx-elementor-frontend' );
}
/**
* Enqueue frontend scripts.
*/
public function enqueue_frontend_scripts() {
wp_enqueue_script( 'rx-elementor-frontend' );
}
/**
* Editor scripts.
*/
public function enqueue_editor_scripts() {
wp_enqueue_script(
'rx-elementor-editor',
get_template_directory_uri() . '/assets/js/elementor/editor.js',
array( 'jquery', 'elementor-editor' ),
$this->version,
true
);
}
/**
* Editor styles.
*/
public function enqueue_editor_styles() {
wp_enqueue_style(
'rx-elementor-editor',
get_template_directory_uri() . '/assets/css/elementor/editor.css',
array(),
$this->version
);
}
/**
* Preview styles.
*/
public function enqueue_preview_styles() {
wp_enqueue_style(
'rx-elementor-preview',
get_template_directory_uri() . '/assets/css/elementor/preview.css',
array(),
$this->version
);
}
/**
* Register RX widget category.
*
* Elementor widget categories organize widgets in the editor panel.
*/
public function register_widget_categories( $elements_manager ) {
$elements_manager->add_category(
'rx-theme',
array(
'title' => esc_html__( 'RX Theme Widgets', 'rx-theme' ),
'icon' => 'fa fa-plug',
)
);
$elements_manager->add_category(
'rx-medical',
array(
'title' => esc_html__( 'RX Medical Widgets', 'rx-theme' ),
'icon' => 'fa fa-heartbeat',
)
);
$elements_manager->add_category(
'rx-seo',
array(
'title' => esc_html__( 'RX SEO Widgets', 'rx-theme' ),
'icon' => 'fa fa-search',
)
);
}
/**
* Register custom Elementor widgets.
*/
public function register_widgets( $widgets_manager ) {
if ( ! class_exists( '\Elementor\Widget_Base' ) ) {
return;
}
/**
* Inline advanced widgets.
* For a bigger theme, you can move each widget class to:
* inc/integrations/elementor/widgets/class-rx-*.php
*/
$this->include_inline_widget_classes();
$widgets = array(
'RX_Elementor_Reading_Time_Widget',
'RX_Elementor_Breadcrumbs_Widget',
'RX_Elementor_Post_Meta_Widget',
'RX_Elementor_Related_Posts_Widget',
'RX_Elementor_Medical_Notice_Widget',
'RX_Elementor_Schema_FAQ_Widget',
'RX_Elementor_Search_Box_Widget',
);
$widgets = apply_filters( 'rx_theme_elementor_widgets', $widgets );
foreach ( $widgets as $widget_class ) {
if ( class_exists( $widget_class ) ) {
$widgets_manager->register( new $widget_class() );
}
}
}
/**
* Register custom document controls.
*/
public function register_document_controls( $document ) {
if ( ! $document || ! method_exists( $document, 'start_controls_section' ) ) {
return;
}
$document->start_controls_section(
'rx_document_settings',
array(
'label' => esc_html__( 'RX Theme Settings', 'rx-theme' ),
'tab' => \Elementor\Controls_Manager::TAB_SETTINGS,
)
);
$document->add_control(
'rx_hide_theme_title',
array(
'label' => esc_html__( 'Hide Theme Page Title', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Yes', 'rx-theme' ),
'label_off' => esc_html__( 'No', 'rx-theme' ),
'return_value' => 'yes',
'default' => '',
)
);
$document->add_control(
'rx_full_width_page',
array(
'label' => esc_html__( 'Force Full Width Layout', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Yes', 'rx-theme' ),
'label_off' => esc_html__( 'No', 'rx-theme' ),
'return_value' => 'yes',
'default' => '',
)
);
$document->add_control(
'rx_disable_breadcrumbs',
array(
'label' => esc_html__( 'Disable Theme Breadcrumbs', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::SWITCHER,
'label_on' => esc_html__( 'Yes', 'rx-theme' ),
'label_off' => esc_html__( 'No', 'rx-theme' ),
'return_value' => 'yes',
'default' => '',
)
);
$document->end_controls_section();
}
/**
* Register container controls.
*/
public function register_container_controls( $element, $args ) {
$this->add_rx_advanced_controls( $element );
}
/**
* Register section controls.
*/
public function register_section_controls( $element, $args ) {
$this->add_rx_advanced_controls( $element );
}
/**
* Register common controls for widgets.
*/
public function register_common_controls( $element, $args ) {
if ( ! $element || ! method_exists( $element, 'start_controls_section' ) ) {
return;
}
$element->start_controls_section(
'rx_accessibility_section',
array(
'label' => esc_html__( 'RX Accessibility & SEO', 'rx-theme' ),
'tab' => \Elementor\Controls_Manager::TAB_ADVANCED,
)
);
$element->add_control(
'rx_aria_label',
array(
'label' => esc_html__( 'ARIA Label', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::TEXT,
'description' => esc_html__( 'Optional accessible label for screen readers.', 'rx-theme' ),
)
);
$element->add_control(
'rx_schema_role',
array(
'label' => esc_html__( 'Semantic Role', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::SELECT,
'default' => '',
'options' => array(
'' => esc_html__( 'Default', 'rx-theme' ),
'banner' => esc_html__( 'Banner', 'rx-theme' ),
'navigation' => esc_html__( 'Navigation', 'rx-theme' ),
'main' => esc_html__( 'Main', 'rx-theme' ),
'complementary'=> esc_html__( 'Complementary', 'rx-theme' ),
'contentinfo' => esc_html__( 'Content Info', 'rx-theme' ),
'region' => esc_html__( 'Region', 'rx-theme' ),
),
)
);
$element->add_control(
'rx_css_print_hide',
array(
'label' => esc_html__( 'Hide When Printing', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::SWITCHER,
'return_value' => 'yes',
'default' => '',
)
);
$element->end_controls_section();
}
/**
* Add advanced controls to sections/containers.
*/
private function add_rx_advanced_controls( $element ) {
if ( ! $element || ! method_exists( $element, 'start_controls_section' ) ) {
return;
}
$element->start_controls_section(
'rx_layout_enhancements',
array(
'label' => esc_html__( 'RX Layout Enhancements', 'rx-theme' ),
'tab' => \Elementor\Controls_Manager::TAB_LAYOUT,
)
);
$element->add_control(
'rx_sticky_safe',
array(
'label' => esc_html__( 'RX Sticky Safe Mode', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::SWITCHER,
'description' => esc_html__( 'Adds safe sticky classes that avoid overflow and z-index problems.', 'rx-theme' ),
'return_value' => 'yes',
'default' => '',
'prefix_class' => 'rx-sticky-safe-',
)
);
$element->add_control(
'rx_content_visibility',
array(
'label' => esc_html__( 'Enable Content Visibility', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::SWITCHER,
'description' => esc_html__( 'Adds browser-level rendering optimization for long pages.', 'rx-theme' ),
'return_value' => 'yes',
'default' => '',
'prefix_class' => 'rx-content-visibility-',
)
);
$element->add_control(
'rx_container_width_mode',
array(
'label' => esc_html__( 'RX Width Mode', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::SELECT,
'default' => '',
'options' => array(
'' => esc_html__( 'Default', 'rx-theme' ),
'narrow' => esc_html__( 'Narrow Reading Width', 'rx-theme' ),
'content' => esc_html__( 'Content Width', 'rx-theme' ),
'wide' => esc_html__( 'Wide Width', 'rx-theme' ),
'full' => esc_html__( 'Full Bleed', 'rx-theme' ),
),
'prefix_class' => 'rx-width-mode-',
)
);
$element->end_controls_section();
}
/**
* Add attributes before Elementor element render.
*/
public function before_element_render( $element ) {
if ( ! $element || ! method_exists( $element, 'get_settings_for_display' ) ) {
return;
}
$settings = $element->get_settings_for_display();
if ( ! empty( $settings['rx_aria_label'] ) && method_exists( $element, 'add_render_attribute' ) ) {
$element->add_render_attribute( '_wrapper', 'aria-label', esc_attr( $settings['rx_aria_label'] ) );
}
if ( ! empty( $settings['rx_schema_role'] ) && method_exists( $element, 'add_render_attribute' ) ) {
$allowed_roles = array(
'banner',
'navigation',
'main',
'complementary',
'contentinfo',
'region',
);
if ( in_array( $settings['rx_schema_role'], $allowed_roles, true ) ) {
$element->add_render_attribute( '_wrapper', 'role', esc_attr( $settings['rx_schema_role'] ) );
}
}
if ( ! empty( $settings['rx_css_print_hide'] ) && 'yes' === $settings['rx_css_print_hide'] && method_exists( $element, 'add_render_attribute' ) ) {
$element->add_render_attribute( '_wrapper', 'class', 'rx-print-hide' );
}
}
/**
* After render hook.
*/
public function after_element_render( $element ) {
/**
* Developer extension point.
*/
do_action( 'rx_theme_elementor_after_element_render', $element );
}
/**
* Filter widget content.
*/
public function filter_widget_render_content( $content, $widget ) {
if ( empty( $content ) || ! is_string( $content ) ) {
return $content;
}
/**
* Optional lazy loading improvement for images rendered by Elementor widgets.
*/
if ( apply_filters( 'rx_theme_elementor_auto_lazy_images', true ) ) {
$content = preg_replace_callback(
'/<img\s+([^>]*?)>/i',
function ( $matches ) {
$img = $matches[0];
if ( false === strpos( $img, ' loading=' ) ) {
$img = str_replace( '<img ', '<img loading="lazy" ', $img );
}
if ( false === strpos( $img, ' decoding=' ) ) {
$img = str_replace( '<img ', '<img decoding="async" ', $img );
}
return $img;
},
$content
);
}
return $content;
}
/**
* Elementor related posts custom query.
*/
public function query_related_posts( $query ) {
if ( ! is_singular( 'post' ) ) {
return;
}
$post_id = get_the_ID();
$cats = wp_get_post_categories( $post_id );
if ( ! empty( $cats ) ) {
$query->set( 'category__in', $cats );
}
$query->set( 'post__not_in', array( $post_id ) );
$query->set( 'ignore_sticky_posts', true );
}
/**
* Latest medical posts query.
*/
public function query_latest_medical_posts( $query ) {
$query->set( 'post_type', 'post' );
$query->set( 'post_status', 'publish' );
$query->set( 'orderby', 'date' );
$query->set( 'order', 'DESC' );
$query->set( 'ignore_sticky_posts', true );
$medical_category = apply_filters( 'rx_theme_medical_category_slug', 'medical' );
if ( $medical_category ) {
$query->set( 'category_name', sanitize_title( $medical_category ) );
}
}
/**
* Popular posts query.
*/
public function query_popular_posts( $query ) {
$query->set( 'post_type', 'post' );
$query->set( 'post_status', 'publish' );
$query->set( 'meta_key', 'rx_post_views' );
$query->set( 'orderby', 'meta_value_num' );
$query->set( 'order', 'DESC' );
$query->set( 'ignore_sticky_posts', true );
}
/**
* Add body classes.
*/
public function body_classes( $classes ) {
if ( did_action( 'elementor/loaded' ) ) {
$classes[] = 'rx-elementor-active';
}
if ( $this->is_elementor_page() ) {
$classes[] = 'rx-is-elementor-page';
}
if ( $this->is_elementor_canvas() ) {
$classes[] = 'rx-elementor-canvas';
}
if ( $this->is_elementor_full_width() ) {
$classes[] = 'rx-elementor-full-width';
}
return array_unique( $classes );
}
/**
* Add post classes.
*/
public function post_classes( $classes, $class, $post_id ) {
if ( $post_id && $this->is_built_with_elementor( $post_id ) ) {
$classes[] = 'rx-post-built-with-elementor';
}
return array_unique( $classes );
}
/**
* Maybe disable theme title/sidebar etc.
*/
public function maybe_disable_theme_parts() {
if ( ! $this->is_elementor_page() ) {
return;
}
/**
* Developers can use these filters in theme template files:
*
* apply_filters( 'rx_theme_show_page_title', true )
* apply_filters( 'rx_theme_show_breadcrumbs', true )
* apply_filters( 'rx_theme_show_sidebar', true )
*/
add_filter( 'rx_theme_show_page_title', array( $this, 'filter_show_page_title' ) );
add_filter( 'rx_theme_show_breadcrumbs', array( $this, 'filter_show_breadcrumbs' ) );
add_filter( 'rx_theme_show_sidebar', array( $this, 'filter_show_sidebar' ) );
}
/**
* Show page title filter.
*/
public function filter_show_page_title( $show ) {
$post_id = get_the_ID();
if ( ! $post_id ) {
return $show;
}
$hide = get_post_meta( $post_id, '_elementor_rx_hide_theme_title', true );
if ( 'yes' === $hide ) {
return false;
}
return $show;
}
/**
* Show breadcrumbs filter.
*/
public function filter_show_breadcrumbs( $show ) {
$post_id = get_the_ID();
if ( ! $post_id ) {
return $show;
}
$disabled = get_post_meta( $post_id, '_elementor_rx_disable_breadcrumbs', true );
if ( 'yes' === $disabled ) {
return false;
}
return $show;
}
/**
* Show sidebar filter.
*/
public function filter_show_sidebar( $show ) {
if ( $this->is_elementor_full_width() || $this->is_elementor_canvas() ) {
return false;
}
return $show;
}
/**
* Missing Elementor admin notice.
*/
public function admin_notice_missing_elementor() {
if ( did_action( 'elementor/loaded' ) ) {
return;
}
if ( ! current_user_can( 'activate_plugins' ) ) {
return;
}
$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
if ( ! $screen || false === strpos( $screen->id, 'appearance' ) ) {
return;
}
echo '<div class="notice notice-info is-dismissible">';
echo '<p>';
echo esc_html__( 'RX Theme is Elementor-ready. For full header, footer, landing page, and widget support, install and activate Elementor.', 'rx-theme' );
echo '</p>';
echo '</div>';
}
/**
* Optional: disable Elementor Google Fonts.
*/
public function maybe_disable_elementor_google_fonts( $load ) {
return apply_filters( 'rx_theme_elementor_load_google_fonts', $load );
}
/**
* Custom CSS file name.
*/
public function elementor_css_file_name( $name, $file ) {
if ( apply_filters( 'rx_theme_elementor_custom_css_file_names', false ) ) {
$name = 'rx-elementor-' . sanitize_file_name( $name );
}
return $name;
}
/**
* Is Elementor page?
*/
private function is_elementor_page() {
$post_id = get_the_ID();
return $post_id && $this->is_built_with_elementor( $post_id );
}
/**
* Is built with Elementor?
*/
private function is_built_with_elementor( $post_id ) {
if ( ! did_action( 'elementor/loaded' ) || ! class_exists( '\Elementor\Plugin' ) ) {
return false;
}
return \Elementor\Plugin::$instance->documents->get( $post_id ) && \Elementor\Plugin::$instance->documents->get( $post_id )->is_built_with_elementor();
}
/**
* Is Elementor canvas template?
*/
private function is_elementor_canvas() {
$post_id = get_the_ID();
if ( ! $post_id ) {
return false;
}
return 'elementor_canvas' === get_page_template_slug( $post_id );
}
/**
* Is Elementor full-width template?
*/
private function is_elementor_full_width() {
$post_id = get_the_ID();
if ( ! $post_id ) {
return false;
}
$template = get_page_template_slug( $post_id );
if ( 'elementor_header_footer' === $template ) {
return true;
}
$full_width = get_post_meta( $post_id, '_elementor_rx_full_width_page', true );
return 'yes' === $full_width;
}
/**
* Output Elementor location with fallback.
*
* Use inside theme templates:
* RX_Elementor_Integration::instance()->do_location( 'header', 'rx_theme_header_fallback' );
*/
public function do_location( $location, $fallback_callback = '' ) {
$location = sanitize_key( $location );
if ( function_exists( 'elementor_theme_do_location' ) && elementor_theme_do_location( $location ) ) {
return true;
}
if ( $fallback_callback && is_callable( $fallback_callback ) ) {
call_user_func( $fallback_callback );
return true;
}
return false;
}
/**
* Include inline widget classes once.
*/
private function include_inline_widget_classes() {
if ( class_exists( 'RX_Elementor_Reading_Time_Widget' ) ) {
return;
}
/**
* Reading Time Widget.
*/
class RX_Elementor_Reading_Time_Widget extends \Elementor\Widget_Base {
public function get_name() {
return 'rx_reading_time';
}
public function get_title() {
return esc_html__( 'RX Reading Time', 'rx-theme' );
}
public function get_icon() {
return 'eicon-clock';
}
public function get_categories() {
return array( 'rx-theme', 'rx-seo' );
}
public function get_keywords() {
return array( 'reading', 'time', 'rx', 'seo', 'article' );
}
protected function register_controls() {
$this->start_controls_section(
'section_content',
array(
'label' => esc_html__( 'Content', 'rx-theme' ),
)
);
$this->add_control(
'prefix',
array(
'label' => esc_html__( 'Prefix', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::TEXT,
'default' => esc_html__( 'Reading time:', 'rx-theme' ),
)
);
$this->add_control(
'words_per_minute',
array(
'label' => esc_html__( 'Words Per Minute', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::NUMBER,
'default' => 200,
'min' => 100,
'max' => 400,
)
);
$this->end_controls_section();
$this->start_controls_section(
'section_style',
array(
'label' => esc_html__( 'Style', 'rx-theme' ),
'tab' => \Elementor\Controls_Manager::TAB_STYLE,
)
);
$this->add_control(
'text_color',
array(
'label' => esc_html__( 'Text Color', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::COLOR,
'selectors' => array(
'{{WRAPPER}} .rx-reading-time' => 'color: {{VALUE}};',
),
)
);
$this->add_group_control(
\Elementor\Group_Control_Typography::get_type(),
array(
'name' => 'typography',
'selector' => '{{WRAPPER}} .rx-reading-time',
)
);
$this->end_controls_section();
}
protected function render() {
$settings = $this->get_settings_for_display();
$post_id = get_the_ID();
if ( ! $post_id ) {
return;
}
$content = get_post_field( 'post_content', $post_id );
$words = str_word_count( wp_strip_all_tags( $content ) );
$wpm = ! empty( $settings['words_per_minute'] ) ? absint( $settings['words_per_minute'] ) : 200;
$minutes = max( 1, ceil( $words / $wpm ) );
echo '<div class="rx-reading-time">';
echo esc_html( $settings['prefix'] ) . ' ';
echo esc_html( sprintf( _n( '%s minute', '%s minutes', $minutes, 'rx-theme' ), number_format_i18n( $minutes ) ) );
echo '</div>';
}
}
/**
* Breadcrumbs Widget.
*/
class RX_Elementor_Breadcrumbs_Widget extends \Elementor\Widget_Base {
public function get_name() {
return 'rx_breadcrumbs';
}
public function get_title() {
return esc_html__( 'RX Breadcrumbs', 'rx-theme' );
}
public function get_icon() {
return 'eicon-navigation-horizontal';
}
public function get_categories() {
return array( 'rx-theme', 'rx-seo' );
}
protected function register_controls() {
$this->start_controls_section(
'section_content',
array(
'label' => esc_html__( 'Content', 'rx-theme' ),
)
);
$this->add_control(
'home_text',
array(
'label' => esc_html__( 'Home Text', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::TEXT,
'default' => esc_html__( 'Home', 'rx-theme' ),
)
);
$this->add_control(
'separator',
array(
'label' => esc_html__( 'Separator', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::TEXT,
'default' => '/',
)
);
$this->end_controls_section();
}
protected function render() {
$settings = $this->get_settings_for_display();
echo '<nav class="rx-breadcrumbs" aria-label="' . esc_attr__( 'Breadcrumbs', 'rx-theme' ) . '">';
echo '<a href="' . esc_url( home_url( '/' ) ) . '">' . esc_html( $settings['home_text'] ) . '</a>';
if ( is_singular() ) {
$post_id = get_the_ID();
$categories = get_the_category( $post_id );
if ( ! empty( $categories ) ) {
$cat = $categories[0];
echo ' <span class="rx-breadcrumb-separator">' . esc_html( $settings['separator'] ) . '</span> ';
echo '<a href="' . esc_url( get_category_link( $cat->term_id ) ) . '">' . esc_html( $cat->name ) . '</a>';
}
echo ' <span class="rx-breadcrumb-separator">' . esc_html( $settings['separator'] ) . '</span> ';
echo '<span aria-current="page">' . esc_html( get_the_title() ) . '</span>';
} elseif ( is_category() || is_tag() || is_tax() ) {
echo ' <span class="rx-breadcrumb-separator">' . esc_html( $settings['separator'] ) . '</span> ';
echo '<span aria-current="page">' . esc_html( single_term_title( '', false ) ) . '</span>';
} elseif ( is_search() ) {
echo ' <span class="rx-breadcrumb-separator">' . esc_html( $settings['separator'] ) . '</span> ';
echo '<span aria-current="page">' . esc_html__( 'Search', 'rx-theme' ) . '</span>';
}
echo '</nav>';
}
}
/**
* Post Meta Widget.
*/
class RX_Elementor_Post_Meta_Widget extends \Elementor\Widget_Base {
public function get_name() {
return 'rx_post_meta';
}
public function get_title() {
return esc_html__( 'RX Post Meta', 'rx-theme' );
}
public function get_icon() {
return 'eicon-post-info';
}
public function get_categories() {
return array( 'rx-theme', 'rx-seo' );
}
protected function register_controls() {
$this->start_controls_section(
'section_content',
array(
'label' => esc_html__( 'Meta Items', 'rx-theme' ),
)
);
$this->add_control(
'show_author',
array(
'label' => esc_html__( 'Show Author', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::SWITCHER,
'default' => 'yes',
'return_value' => 'yes',
)
);
$this->add_control(
'show_date',
array(
'label' => esc_html__( 'Show Date', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::SWITCHER,
'default' => 'yes',
'return_value' => 'yes',
)
);
$this->add_control(
'show_category',
array(
'label' => esc_html__( 'Show Category', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::SWITCHER,
'default' => 'yes',
'return_value' => 'yes',
)
);
$this->end_controls_section();
}
protected function render() {
$settings = $this->get_settings_for_display();
echo '<div class="rx-post-meta">';
if ( 'yes' === $settings['show_author'] ) {
echo '<span class="rx-meta-author">' . esc_html__( 'By', 'rx-theme' ) . ' ';
echo '<a href="' . esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ) . '">' . esc_html( get_the_author() ) . '</a>';
echo '</span>';
}
if ( 'yes' === $settings['show_date'] ) {
echo '<span class="rx-meta-date">';
echo '<time datetime="' . esc_attr( get_the_date( DATE_W3C ) ) . '">' . esc_html( get_the_date() ) . '</time>';
echo '</span>';
}
if ( 'yes' === $settings['show_category'] ) {
$categories = get_the_category();
if ( ! empty( $categories ) ) {
echo '<span class="rx-meta-category">';
echo '<a href="' . esc_url( get_category_link( $categories[0]->term_id ) ) . '">' . esc_html( $categories[0]->name ) . '</a>';
echo '</span>';
}
}
echo '</div>';
}
}
/**
* Related Posts Widget.
*/
class RX_Elementor_Related_Posts_Widget extends \Elementor\Widget_Base {
public function get_name() {
return 'rx_related_posts';
}
public function get_title() {
return esc_html__( 'RX Related Posts', 'rx-theme' );
}
public function get_icon() {
return 'eicon-post-list';
}
public function get_categories() {
return array( 'rx-theme', 'rx-seo' );
}
protected function register_controls() {
$this->start_controls_section(
'section_content',
array(
'label' => esc_html__( 'Content', 'rx-theme' ),
)
);
$this->add_control(
'title',
array(
'label' => esc_html__( 'Title', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::TEXT,
'default' => esc_html__( 'Related Articles', 'rx-theme' ),
)
);
$this->add_control(
'posts_per_page',
array(
'label' => esc_html__( 'Posts Per Page', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::NUMBER,
'default' => 4,
'min' => 1,
'max' => 12,
)
);
$this->end_controls_section();
}
protected function render() {
if ( ! is_singular( 'post' ) ) {
return;
}
$settings = $this->get_settings_for_display();
$post_id = get_the_ID();
$cats = wp_get_post_categories( $post_id );
$args = array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => absint( $settings['posts_per_page'] ),
'post__not_in' => array( $post_id ),
'ignore_sticky_posts' => true,
);
if ( ! empty( $cats ) ) {
$args['category__in'] = $cats;
}
$query = new WP_Query( $args );
if ( ! $query->have_posts() ) {
return;
}
echo '<section class="rx-related-posts">';
echo '<h2 class="rx-related-posts-title">' . esc_html( $settings['title'] ) . '</h2>';
echo '<div class="rx-related-posts-grid">';
while ( $query->have_posts() ) {
$query->the_post();
echo '<article class="rx-related-post-card">';
if ( has_post_thumbnail() ) {
echo '<a class="rx-related-post-thumb" href="' . esc_url( get_permalink() ) . '">';
the_post_thumbnail( 'medium_large', array( 'loading' => 'lazy', 'decoding' => 'async' ) );
echo '</a>';
}
echo '<h3><a href="' . esc_url( get_permalink() ) . '">' . esc_html( get_the_title() ) . '</a></h3>';
echo '<p>' . esc_html( wp_trim_words( get_the_excerpt(), 18 ) ) . '</p>';
echo '</article>';
}
echo '</div>';
echo '</section>';
wp_reset_postdata();
}
}
/**
* Medical Notice Widget.
*/
class RX_Elementor_Medical_Notice_Widget extends \Elementor\Widget_Base {
public function get_name() {
return 'rx_medical_notice';
}
public function get_title() {
return esc_html__( 'RX Medical Notice', 'rx-theme' );
}
public function get_icon() {
return 'eicon-alert';
}
public function get_categories() {
return array( 'rx-medical' );
}
protected function register_controls() {
$this->start_controls_section(
'section_content',
array(
'label' => esc_html__( 'Notice', 'rx-theme' ),
)
);
$this->add_control(
'title',
array(
'label' => esc_html__( 'Title', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::TEXT,
'default' => esc_html__( 'Medical Disclaimer', 'rx-theme' ),
)
);
$this->add_control(
'notice',
array(
'label' => esc_html__( 'Notice Text', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::TEXTAREA,
'default' => esc_html__( 'This article is for educational purposes only and is not a substitute for professional medical advice, diagnosis, or treatment.', 'rx-theme' ),
)
);
$this->end_controls_section();
}
protected function render() {
$settings = $this->get_settings_for_display();
echo '<aside class="rx-medical-notice" role="note">';
echo '<strong>' . esc_html( $settings['title'] ) . '</strong>';
echo '<p>' . esc_html( $settings['notice'] ) . '</p>';
echo '</aside>';
}
}
/**
* FAQ Schema Widget.
*/
class RX_Elementor_Schema_FAQ_Widget extends \Elementor\Widget_Base {
public function get_name() {
return 'rx_schema_faq';
}
public function get_title() {
return esc_html__( 'RX FAQ Schema', 'rx-theme' );
}
public function get_icon() {
return 'eicon-help-o';
}
public function get_categories() {
return array( 'rx-seo' );
}
protected function register_controls() {
$this->start_controls_section(
'section_content',
array(
'label' => esc_html__( 'FAQs', 'rx-theme' ),
)
);
$repeater = new \Elementor\Repeater();
$repeater->add_control(
'question',
array(
'label' => esc_html__( 'Question', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::TEXT,
)
);
$repeater->add_control(
'answer',
array(
'label' => esc_html__( 'Answer', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::TEXTAREA,
)
);
$this->add_control(
'faqs',
array(
'label' => esc_html__( 'FAQ Items', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::REPEATER,
'fields' => $repeater->get_controls(),
'title_field' => '{{{ question }}}',
)
);
$this->end_controls_section();
}
protected function render() {
$settings = $this->get_settings_for_display();
if ( empty( $settings['faqs'] ) ) {
return;
}
$schema = array(
'@context' => 'https://schema.org',
'@type' => 'FAQPage',
'mainEntity' => array(),
);
echo '<div class="rx-faq-schema-widget">';
foreach ( $settings['faqs'] as $index => $faq ) {
if ( empty( $faq['question'] ) || empty( $faq['answer'] ) ) {
continue;
}
$question = wp_strip_all_tags( $faq['question'] );
$answer = wp_strip_all_tags( $faq['answer'] );
$schema['mainEntity'][] = array(
'@type' => 'Question',
'name' => $question,
'acceptedAnswer' => array(
'@type' => 'Answer',
'text' => $answer,
),
);
echo '<details class="rx-faq-item">';
echo '<summary>' . esc_html( $question ) . '</summary>';
echo '<div class="rx-faq-answer">' . wpautop( esc_html( $answer ) ) . '</div>';
echo '</details>';
}
echo '<script type="application/ld+json">' . wp_json_encode( $schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . '</script>';
echo '</div>';
}
}
/**
* Search Box Widget.
*/
class RX_Elementor_Search_Box_Widget extends \Elementor\Widget_Base {
public function get_name() {
return 'rx_search_box';
}
public function get_title() {
return esc_html__( 'RX Search Box', 'rx-theme' );
}
public function get_icon() {
return 'eicon-search';
}
public function get_categories() {
return array( 'rx-theme' );
}
protected function register_controls() {
$this->start_controls_section(
'section_content',
array(
'label' => esc_html__( 'Search', 'rx-theme' ),
)
);
$this->add_control(
'placeholder',
array(
'label' => esc_html__( 'Placeholder', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::TEXT,
'default' => esc_html__( 'Search medical articles...', 'rx-theme' ),
)
);
$this->add_control(
'button_text',
array(
'label' => esc_html__( 'Button Text', 'rx-theme' ),
'type' => \Elementor\Controls_Manager::TEXT,
'default' => esc_html__( 'Search', 'rx-theme' ),
)
);
$this->end_controls_section();
}
protected function render() {
$settings = $this->get_settings_for_display();
echo '<form role="search" method="get" class="rx-elementor-search-form" action="' . esc_url( home_url( '/' ) ) . '">';
echo '<label class="screen-reader-text" for="rx-elementor-search-' . esc_attr( $this->get_id() ) . '">' . esc_html__( 'Search for:', 'rx-theme' ) . '</label>';
echo '<input id="rx-elementor-search-' . esc_attr( $this->get_id() ) . '" type="search" name="s" value="' . esc_attr( get_search_query() ) . '" placeholder="' . esc_attr( $settings['placeholder'] ) . '">';
echo '<button type="submit">' . esc_html( $settings['button_text'] ) . '</button>';
echo '</form>';
}
}
}
}
endif;
/**
* Initialize RX Elementor integration.
*/
RX_Elementor_Integration::instance();
/**
* Helper function for templates.
*
* Example:
* rx_elementor_location( 'header', 'rx_theme_header_fallback' );
*/
if ( ! function_exists( 'rx_elementor_location' ) ) {
function rx_elementor_location( $location, $fallback_callback = '' ) {
return RX_Elementor_Integration::instance()->do_location( $location, $fallback_callback );
}
}
Add this in your functions.php:
require_once get_template_directory() . '/inc/integrations/elementor.php';
Recommended folders/files to create for assets:
rx-theme/
└── assets/
├── css/
│ └── elementor/
│ ├── frontend.css
│ ├── editor.css
│ └── preview.css
└── js/
└── elementor/
├── frontend.js
└── editor.js
Optional starter assets/css/elementor/frontend.css:
.rx-reading-time,
.rx-post-meta,
.rx-breadcrumbs {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
}
.rx-related-posts-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1.5rem;
}
.rx-related-post-card {
border: 1px solid currentColor;
border-radius: 12px;
padding: 1rem;
}
.rx-related-post-thumb img {
width: 100%;
height: auto;
border-radius: 10px;
}
.rx-medical-notice {
padding: 1rem;
border-inline-start: 4px solid currentColor;
border-radius: 10px;
background: rgba(0, 0, 0, 0.04);
}
.rx-faq-item {
margin-bottom: 0.75rem;
padding: 1rem;
border: 1px solid currentColor;
border-radius: 10px;
}
.rx-elementor-search-form {
display: flex;
gap: 0.5rem;
}
.rx-elementor-search-form input[type="search"] {
width: 100%;
min-height: 44px;
}
.rx-elementor-search-form button {
min-height: 44px;
cursor: pointer;
}
.rx-width-mode-narrow {
max-width: 760px;
margin-inline: auto;
}
.rx-width-mode-content {
max-width: 1140px;
margin-inline: auto;
}
.rx-width-mode-wide {
max-width: 1320px;
margin-inline: auto;
}
.rx-width-mode-full {
max-width: none;
width: 100%;
}
.rx-content-visibility-yes {
content-visibility: auto;
contain-intrinsic-size: 1px 800px;
}
@media print {
.rx-print-hide {
display: none !important;
}
}
Optional starter assets/js/elementor/frontend.js:
(function ($) {
'use strict';
$(window).on('elementor/frontend/init', function () {
if (typeof elementorFrontend === 'undefined') {
return;
}
elementorFrontend.hooks.addAction('frontend/element_ready/global', function ($scope) {
$scope.find('img:not([loading])').attr('loading', 'lazy');
$scope.find('img:not([decoding])').attr('decoding', 'async');
});
});
})(jQuery);
One important note: the custom theme locations like rx_top_bar, rx_before_content, and rx_sidebar only work visually when you call them in your theme template files with:
rx_elementor_location( 'rx_top_bar' );
rx_elementor_location( 'rx_before_content' );
rx_elementor_location( 'rx_sidebar' );
Elementor’s official docs show that registered locations are displayed with elementor_theme_do_location(), and this file wraps that in the safer rx_elementor_location() helper.