footer-builder.php

I’ll build this as a powerful footer-builder.php module for your RX theme: hooks, widget areas, footer columns, copyright, menus, social links, back-to-top, schema, customizer settings, sanitization, and frontend output in one place.

My dear friend, below is an advanced inc/builders/footer-builder.php file for your RX WordPress theme. It includes many footer functions: footer columns, widgets, menus, copyright, social icons, back-to-top button, footer CTA, contact info, schema markup, Customizer settings, sanitization, accessibility, hooks, and performance-friendly output.

Create this file:

rx-theme/inc/builders/footer-builder.php

Then include it in your functions.php:

require_once get_template_directory() . '/inc/builders/footer-builder.php';

inc/builders/footer-builder.php

<?php
/**
 * RX Theme Footer Builder
 *
 * Advanced footer builder for RX Theme.
 *
 * Features:
 * - Footer widget areas
 * - Footer menu locations
 * - Dynamic footer columns
 * - Copyright text
 * - Social links
 * - Back to top button
 * - Footer CTA
 * - Contact info section
 * - Schema.org organization markup
 * - Accessibility friendly footer
 * - Customizer controls
 * - Safe sanitization
 *
 * @package RX_Theme
 */

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

if ( ! class_exists( 'RX_Footer_Builder' ) ) :

	final class RX_Footer_Builder {

		/**
		 * Instance.
		 *
		 * @var RX_Footer_Builder|null
		 */
		private static $instance = null;

		/**
		 * Get instance.
		 *
		 * @return RX_Footer_Builder
		 */
		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, 'register_footer_menus' ) );
			add_action( 'widgets_init', array( $this, 'register_footer_widgets' ) );
			add_action( 'customize_register', array( $this, 'register_customizer_options' ) );
			add_action( 'wp_footer', array( $this, 'render_back_to_top' ), 20 );
			add_action( 'wp_footer', array( $this, 'render_footer_schema' ), 30 );

			add_action( 'rx_theme_footer', array( $this, 'render_footer' ) );

			add_filter( 'body_class', array( $this, 'footer_body_classes' ) );
		}

		/**
		 * Default option values.
		 *
		 * @return array
		 */
		public static function defaults() {
			return array(
				'rx_footer_enable'              => true,
				'rx_footer_layout'              => 'layout-1',
				'rx_footer_width'               => 'contained',
				'rx_footer_columns'             => 4,
				'rx_footer_show_widgets'        => true,
				'rx_footer_show_menu'           => true,
				'rx_footer_show_social'         => true,
				'rx_footer_show_contact'        => true,
				'rx_footer_show_cta'            => false,
				'rx_footer_show_bottom_bar'     => true,
				'rx_footer_show_back_to_top'    => true,

				'rx_footer_bg_color'            => '#101827',
				'rx_footer_text_color'          => '#e5e7eb',
				'rx_footer_link_color'          => '#ffffff',
				'rx_footer_link_hover_color'    => '#38bdf8',
				'rx_footer_border_color'        => '#1f2937',

				'rx_footer_cta_title'           => 'Ready to improve your health knowledge?',
				'rx_footer_cta_text'            => 'Explore trusted, simple, and evidence-based health articles.',
				'rx_footer_cta_button_text'     => 'Explore Articles',
				'rx_footer_cta_button_url'      => home_url( '/' ),

				'rx_footer_contact_title'       => 'Contact',
				'rx_footer_contact_address'     => '',
				'rx_footer_contact_phone'       => '',
				'rx_footer_contact_email'       => get_option( 'admin_email' ),

				'rx_footer_facebook'            => '',
				'rx_footer_twitter'             => '',
				'rx_footer_linkedin'            => '',
				'rx_footer_youtube'             => '',
				'rx_footer_instagram'           => '',
				'rx_footer_pinterest'           => '',
				'rx_footer_github'              => '',
				'rx_footer_whatsapp'            => '',
				'rx_footer_telegram'            => '',

				'rx_footer_copyright'           => '© {year} {site_name}. All rights reserved.',
				'rx_footer_credit_enable'       => true,
				'rx_footer_credit_text'         => 'Built with RX Theme',

				'rx_footer_schema_enable'       => true,
				'rx_footer_org_name'            => get_bloginfo( 'name' ),
				'rx_footer_org_logo'            => '',
				'rx_footer_org_url'             => home_url( '/' ),
			);
		}

		/**
		 * Get option with default fallback.
		 *
		 * @param string $key Option key.
		 * @return mixed
		 */
		public static function get_option( $key ) {
			$defaults = self::defaults();
			$default  = isset( $defaults[ $key ] ) ? $defaults[ $key ] : '';

			return get_theme_mod( $key, $default );
		}

		/**
		 * Register footer menus.
		 *
		 * @return void
		 */
		public function register_footer_menus() {
			register_nav_menus(
				array(
					'rx_footer_menu'        => esc_html__( 'RX Footer Menu', 'rx-theme' ),
					'rx_footer_bottom_menu' => esc_html__( 'RX Footer Bottom Menu', 'rx-theme' ),
					'rx_footer_legal_menu'  => esc_html__( 'RX Footer Legal Menu', 'rx-theme' ),
				)
			);
		}

		/**
		 * Register footer widget areas.
		 *
		 * @return void
		 */
		public function register_footer_widgets() {
			$columns = 6;

			for ( $i = 1; $i <= $columns; $i++ ) {
				register_sidebar(
					array(
						'name'          => sprintf( esc_html__( 'RX Footer Column %d', 'rx-theme' ), $i ),
						'id'            => 'rx-footer-' . $i,
						'description'   => sprintf( esc_html__( 'Add widgets for footer column %d.', 'rx-theme' ), $i ),
						'before_widget' => '<section id="%1$s" class="rx-footer-widget widget %2$s">',
						'after_widget'  => '</section>',
						'before_title'  => '<h2 class="rx-footer-widget-title">',
						'after_title'   => '</h2>',
					)
				);
			}
		}

		/**
		 * Add body classes.
		 *
		 * @param array $classes Body classes.
		 * @return array
		 */
		public function footer_body_classes( $classes ) {
			$layout  = sanitize_html_class( self::get_option( 'rx_footer_layout' ) );
			$width   = sanitize_html_class( self::get_option( 'rx_footer_width' ) );
			$columns = absint( self::get_option( 'rx_footer_columns' ) );

			$classes[] = 'rx-footer-' . $layout;
			$classes[] = 'rx-footer-width-' . $width;
			$classes[] = 'rx-footer-columns-' . $columns;

			return $classes;
		}

		/**
		 * Register customizer options.
		 *
		 * @param WP_Customize_Manager $wp_customize Customizer object.
		 * @return void
		 */
		public function register_customizer_options( $wp_customize ) {

			$wp_customize->add_panel(
				'rx_footer_panel',
				array(
					'title'       => esc_html__( 'RX Footer Builder', 'rx-theme' ),
					'description' => esc_html__( 'Advanced footer builder options for RX Theme.', 'rx-theme' ),
					'priority'    => 160,
				)
			);

			$this->customizer_general_section( $wp_customize );
			$this->customizer_style_section( $wp_customize );
			$this->customizer_cta_section( $wp_customize );
			$this->customizer_contact_section( $wp_customize );
			$this->customizer_social_section( $wp_customize );
			$this->customizer_bottom_section( $wp_customize );
			$this->customizer_schema_section( $wp_customize );
		}

		/**
		 * General customizer section.
		 *
		 * @param WP_Customize_Manager $wp_customize Customizer object.
		 * @return void
		 */
		private function customizer_general_section( $wp_customize ) {
			$wp_customize->add_section(
				'rx_footer_general_section',
				array(
					'title' => esc_html__( 'Footer General', 'rx-theme' ),
					'panel' => 'rx_footer_panel',
				)
			);

			$this->add_checkbox_control(
				$wp_customize,
				'rx_footer_enable',
				esc_html__( 'Enable Footer', 'rx-theme' ),
				'rx_footer_general_section'
			);

			$this->add_select_control(
				$wp_customize,
				'rx_footer_layout',
				esc_html__( 'Footer Layout', 'rx-theme' ),
				'rx_footer_general_section',
				array(
					'layout-1' => esc_html__( 'Layout 1 - Classic', 'rx-theme' ),
					'layout-2' => esc_html__( 'Layout 2 - Centered', 'rx-theme' ),
					'layout-3' => esc_html__( 'Layout 3 - Minimal', 'rx-theme' ),
					'layout-4' => esc_html__( 'Layout 4 - CTA First', 'rx-theme' ),
				)
			);

			$this->add_select_control(
				$wp_customize,
				'rx_footer_width',
				esc_html__( 'Footer Width', 'rx-theme' ),
				'rx_footer_general_section',
				array(
					'contained' => esc_html__( 'Contained', 'rx-theme' ),
					'full'      => esc_html__( 'Full Width', 'rx-theme' ),
				)
			);

			$this->add_select_control(
				$wp_customize,
				'rx_footer_columns',
				esc_html__( 'Footer Columns', 'rx-theme' ),
				'rx_footer_general_section',
				array(
					1 => esc_html__( '1 Column', 'rx-theme' ),
					2 => esc_html__( '2 Columns', 'rx-theme' ),
					3 => esc_html__( '3 Columns', 'rx-theme' ),
					4 => esc_html__( '4 Columns', 'rx-theme' ),
					5 => esc_html__( '5 Columns', 'rx-theme' ),
					6 => esc_html__( '6 Columns', 'rx-theme' ),
				)
			);

			$checks = array(
				'rx_footer_show_widgets'     => esc_html__( 'Show Footer Widgets', 'rx-theme' ),
				'rx_footer_show_menu'        => esc_html__( 'Show Footer Menu', 'rx-theme' ),
				'rx_footer_show_social'      => esc_html__( 'Show Social Links', 'rx-theme' ),
				'rx_footer_show_contact'     => esc_html__( 'Show Contact Info', 'rx-theme' ),
				'rx_footer_show_cta'         => esc_html__( 'Show Footer CTA', 'rx-theme' ),
				'rx_footer_show_bottom_bar'  => esc_html__( 'Show Bottom Bar', 'rx-theme' ),
				'rx_footer_show_back_to_top' => esc_html__( 'Show Back To Top Button', 'rx-theme' ),
			);

			foreach ( $checks as $key => $label ) {
				$this->add_checkbox_control( $wp_customize, $key, $label, 'rx_footer_general_section' );
			}
		}

		/**
		 * Style customizer section.
		 *
		 * @param WP_Customize_Manager $wp_customize Customizer object.
		 * @return void
		 */
		private function customizer_style_section( $wp_customize ) {
			$wp_customize->add_section(
				'rx_footer_style_section',
				array(
					'title' => esc_html__( 'Footer Style', 'rx-theme' ),
					'panel' => 'rx_footer_panel',
				)
			);

			$colors = array(
				'rx_footer_bg_color'         => esc_html__( 'Background Color', 'rx-theme' ),
				'rx_footer_text_color'       => esc_html__( 'Text Color', 'rx-theme' ),
				'rx_footer_link_color'       => esc_html__( 'Link Color', 'rx-theme' ),
				'rx_footer_link_hover_color' => esc_html__( 'Link Hover Color', 'rx-theme' ),
				'rx_footer_border_color'     => esc_html__( 'Border Color', 'rx-theme' ),
			);

			foreach ( $colors as $key => $label ) {
				$wp_customize->add_setting(
					$key,
					array(
						'default'           => self::defaults()[ $key ],
						'sanitize_callback' => 'sanitize_hex_color',
						'transport'         => 'refresh',
					)
				);

				$wp_customize->add_control(
					new WP_Customize_Color_Control(
						$wp_customize,
						$key,
						array(
							'label'   => $label,
							'section' => 'rx_footer_style_section',
						)
					)
				);
			}
		}

		/**
		 * CTA customizer section.
		 *
		 * @param WP_Customize_Manager $wp_customize Customizer object.
		 * @return void
		 */
		private function customizer_cta_section( $wp_customize ) {
			$wp_customize->add_section(
				'rx_footer_cta_section',
				array(
					'title' => esc_html__( 'Footer CTA', 'rx-theme' ),
					'panel' => 'rx_footer_panel',
				)
			);

			$this->add_text_control( $wp_customize, 'rx_footer_cta_title', esc_html__( 'CTA Title', 'rx-theme' ), 'rx_footer_cta_section' );
			$this->add_textarea_control( $wp_customize, 'rx_footer_cta_text', esc_html__( 'CTA Text', 'rx-theme' ), 'rx_footer_cta_section' );
			$this->add_text_control( $wp_customize, 'rx_footer_cta_button_text', esc_html__( 'CTA Button Text', 'rx-theme' ), 'rx_footer_cta_section' );
			$this->add_url_control( $wp_customize, 'rx_footer_cta_button_url', esc_html__( 'CTA Button URL', 'rx-theme' ), 'rx_footer_cta_section' );
		}

		/**
		 * Contact customizer section.
		 *
		 * @param WP_Customize_Manager $wp_customize Customizer object.
		 * @return void
		 */
		private function customizer_contact_section( $wp_customize ) {
			$wp_customize->add_section(
				'rx_footer_contact_section',
				array(
					'title' => esc_html__( 'Footer Contact', 'rx-theme' ),
					'panel' => 'rx_footer_panel',
				)
			);

			$this->add_text_control( $wp_customize, 'rx_footer_contact_title', esc_html__( 'Contact Title', 'rx-theme' ), 'rx_footer_contact_section' );
			$this->add_textarea_control( $wp_customize, 'rx_footer_contact_address', esc_html__( 'Address', 'rx-theme' ), 'rx_footer_contact_section' );
			$this->add_text_control( $wp_customize, 'rx_footer_contact_phone', esc_html__( 'Phone', 'rx-theme' ), 'rx_footer_contact_section' );
			$this->add_email_control( $wp_customize, 'rx_footer_contact_email', esc_html__( 'Email', 'rx-theme' ), 'rx_footer_contact_section' );
		}

		/**
		 * Social customizer section.
		 *
		 * @param WP_Customize_Manager $wp_customize Customizer object.
		 * @return void
		 */
		private function customizer_social_section( $wp_customize ) {
			$wp_customize->add_section(
				'rx_footer_social_section',
				array(
					'title' => esc_html__( 'Footer Social Links', 'rx-theme' ),
					'panel' => 'rx_footer_panel',
				)
			);

			$socials = $this->social_platforms();

			foreach ( $socials as $key => $data ) {
				$this->add_url_control(
					$wp_customize,
					$key,
					$data['label'],
					'rx_footer_social_section'
				);
			}
		}

		/**
		 * Bottom bar customizer section.
		 *
		 * @param WP_Customize_Manager $wp_customize Customizer object.
		 * @return void
		 */
		private function customizer_bottom_section( $wp_customize ) {
			$wp_customize->add_section(
				'rx_footer_bottom_section',
				array(
					'title' => esc_html__( 'Footer Bottom Bar', 'rx-theme' ),
					'panel' => 'rx_footer_panel',
				)
			);

			$this->add_textarea_control( $wp_customize, 'rx_footer_copyright', esc_html__( 'Copyright Text', 'rx-theme' ), 'rx_footer_bottom_section' );
			$this->add_checkbox_control( $wp_customize, 'rx_footer_credit_enable', esc_html__( 'Show Theme Credit', 'rx-theme' ), 'rx_footer_bottom_section' );
			$this->add_text_control( $wp_customize, 'rx_footer_credit_text', esc_html__( 'Credit Text', 'rx-theme' ), 'rx_footer_bottom_section' );
		}

		/**
		 * Schema customizer section.
		 *
		 * @param WP_Customize_Manager $wp_customize Customizer object.
		 * @return void
		 */
		private function customizer_schema_section( $wp_customize ) {
			$wp_customize->add_section(
				'rx_footer_schema_section',
				array(
					'title' => esc_html__( 'Footer Schema', 'rx-theme' ),
					'panel' => 'rx_footer_panel',
				)
			);

			$this->add_checkbox_control( $wp_customize, 'rx_footer_schema_enable', esc_html__( 'Enable Organization Schema', 'rx-theme' ), 'rx_footer_schema_section' );
			$this->add_text_control( $wp_customize, 'rx_footer_org_name', esc_html__( 'Organization Name', 'rx-theme' ), 'rx_footer_schema_section' );
			$this->add_url_control( $wp_customize, 'rx_footer_org_logo', esc_html__( 'Organization Logo URL', 'rx-theme' ), 'rx_footer_schema_section' );
			$this->add_url_control( $wp_customize, 'rx_footer_org_url', esc_html__( 'Organization URL', 'rx-theme' ), 'rx_footer_schema_section' );
		}

		/**
		 * Add checkbox control.
		 *
		 * @param WP_Customize_Manager $wp_customize Customizer object.
		 * @param string               $key Key.
		 * @param string               $label Label.
		 * @param string               $section Section.
		 * @return void
		 */
		private function add_checkbox_control( $wp_customize, $key, $label, $section ) {
			$wp_customize->add_setting(
				$key,
				array(
					'default'           => self::defaults()[ $key ],
					'sanitize_callback' => array( $this, 'sanitize_checkbox' ),
					'transport'         => 'refresh',
				)
			);

			$wp_customize->add_control(
				$key,
				array(
					'type'    => 'checkbox',
					'label'   => $label,
					'section' => $section,
				)
			);
		}

		/**
		 * Add select control.
		 *
		 * @param WP_Customize_Manager $wp_customize Customizer object.
		 * @param string               $key Key.
		 * @param string               $label Label.
		 * @param string               $section Section.
		 * @param array                $choices Choices.
		 * @return void
		 */
		private function add_select_control( $wp_customize, $key, $label, $section, $choices ) {
			$wp_customize->add_setting(
				$key,
				array(
					'default'           => self::defaults()[ $key ],
					'sanitize_callback' => array( $this, 'sanitize_select' ),
					'transport'         => 'refresh',
				)
			);

			$wp_customize->add_control(
				$key,
				array(
					'type'    => 'select',
					'label'   => $label,
					'section' => $section,
					'choices' => $choices,
				)
			);
		}

		/**
		 * Add text control.
		 *
		 * @param WP_Customize_Manager $wp_customize Customizer object.
		 * @param string               $key Key.
		 * @param string               $label Label.
		 * @param string               $section Section.
		 * @return void
		 */
		private function add_text_control( $wp_customize, $key, $label, $section ) {
			$wp_customize->add_setting(
				$key,
				array(
					'default'           => self::defaults()[ $key ],
					'sanitize_callback' => 'sanitize_text_field',
					'transport'         => 'refresh',
				)
			);

			$wp_customize->add_control(
				$key,
				array(
					'type'    => 'text',
					'label'   => $label,
					'section' => $section,
				)
			);
		}

		/**
		 * Add textarea control.
		 *
		 * @param WP_Customize_Manager $wp_customize Customizer object.
		 * @param string               $key Key.
		 * @param string               $label Label.
		 * @param string               $section Section.
		 * @return void
		 */
		private function add_textarea_control( $wp_customize, $key, $label, $section ) {
			$wp_customize->add_setting(
				$key,
				array(
					'default'           => self::defaults()[ $key ],
					'sanitize_callback' => 'wp_kses_post',
					'transport'         => 'refresh',
				)
			);

			$wp_customize->add_control(
				$key,
				array(
					'type'    => 'textarea',
					'label'   => $label,
					'section' => $section,
				)
			);
		}

		/**
		 * Add URL control.
		 *
		 * @param WP_Customize_Manager $wp_customize Customizer object.
		 * @param string               $key Key.
		 * @param string               $label Label.
		 * @param string               $section Section.
		 * @return void
		 */
		private function add_url_control( $wp_customize, $key, $label, $section ) {
			$wp_customize->add_setting(
				$key,
				array(
					'default'           => self::defaults()[ $key ],
					'sanitize_callback' => 'esc_url_raw',
					'transport'         => 'refresh',
				)
			);

			$wp_customize->add_control(
				$key,
				array(
					'type'    => 'url',
					'label'   => $label,
					'section' => $section,
				)
			);
		}

		/**
		 * Add email control.
		 *
		 * @param WP_Customize_Manager $wp_customize Customizer object.
		 * @param string               $key Key.
		 * @param string               $label Label.
		 * @param string               $section Section.
		 * @return void
		 */
		private function add_email_control( $wp_customize, $key, $label, $section ) {
			$wp_customize->add_setting(
				$key,
				array(
					'default'           => self::defaults()[ $key ],
					'sanitize_callback' => 'sanitize_email',
					'transport'         => 'refresh',
				)
			);

			$wp_customize->add_control(
				$key,
				array(
					'type'    => 'email',
					'label'   => $label,
					'section' => $section,
				)
			);
		}

		/**
		 * Sanitize checkbox.
		 *
		 * @param mixed $checked Value.
		 * @return bool
		 */
		public function sanitize_checkbox( $checked ) {
			return ( isset( $checked ) && true === (bool) $checked );
		}

		/**
		 * Sanitize select.
		 *
		 * @param mixed $input Input.
		 * @param mixed $setting Setting.
		 * @return mixed
		 */
		public function sanitize_select( $input, $setting ) {
			$control = $setting->manager->get_control( $setting->id );

			if ( ! $control ) {
				return $setting->default;
			}

			$choices = $control->choices;

			return array_key_exists( $input, $choices ) ? $input : $setting->default;
		}

		/**
		 * Social platforms.
		 *
		 * @return array
		 */
		private function social_platforms() {
			return array(
				'rx_footer_facebook'  => array(
					'label' => esc_html__( 'Facebook URL', 'rx-theme' ),
					'name'  => 'Facebook',
				),
				'rx_footer_twitter'   => array(
					'label' => esc_html__( 'X / Twitter URL', 'rx-theme' ),
					'name'  => 'X',
				),
				'rx_footer_linkedin'  => array(
					'label' => esc_html__( 'LinkedIn URL', 'rx-theme' ),
					'name'  => 'LinkedIn',
				),
				'rx_footer_youtube'   => array(
					'label' => esc_html__( 'YouTube URL', 'rx-theme' ),
					'name'  => 'YouTube',
				),
				'rx_footer_instagram' => array(
					'label' => esc_html__( 'Instagram URL', 'rx-theme' ),
					'name'  => 'Instagram',
				),
				'rx_footer_pinterest' => array(
					'label' => esc_html__( 'Pinterest URL', 'rx-theme' ),
					'name'  => 'Pinterest',
				),
				'rx_footer_github'    => array(
					'label' => esc_html__( 'GitHub URL', 'rx-theme' ),
					'name'  => 'GitHub',
				),
				'rx_footer_whatsapp'  => array(
					'label' => esc_html__( 'WhatsApp URL', 'rx-theme' ),
					'name'  => 'WhatsApp',
				),
				'rx_footer_telegram'  => array(
					'label' => esc_html__( 'Telegram URL', 'rx-theme' ),
					'name'  => 'Telegram',
				),
			);
		}

		/**
		 * Render footer.
		 *
		 * Use this hook in footer.php:
		 * do_action( 'rx_theme_footer' );
		 *
		 * @return void
		 */
		public function render_footer() {
			if ( ! self::get_option( 'rx_footer_enable' ) ) {
				return;
			}

			$layout  = sanitize_html_class( self::get_option( 'rx_footer_layout' ) );
			$width   = sanitize_html_class( self::get_option( 'rx_footer_width' ) );
			$columns = absint( self::get_option( 'rx_footer_columns' ) );

			if ( $columns < 1 || $columns > 6 ) {
				$columns = 4;
			}

			$style = $this->footer_inline_style();

			?>
			<footer id="colophon"
				class="rx-site-footer rx-footer-builder rx-footer-<?php echo esc_attr( $layout ); ?> rx-footer-width-<?php echo esc_attr( $width ); ?> rx-footer-cols-<?php echo esc_attr( $columns ); ?>"
				role="contentinfo"
				<?php echo $style; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
			>
				<?php do_action( 'rx_before_footer_inner' ); ?>

				<div class="<?php echo esc_attr( $this->container_class() ); ?>">

					<?php
					if ( self::get_option( 'rx_footer_show_cta' ) ) {
						$this->render_cta();
					}

					if ( self::get_option( 'rx_footer_show_widgets' ) ) {
						$this->render_widget_columns( $columns );
					}

					if ( self::get_option( 'rx_footer_show_contact' ) || self::get_option( 'rx_footer_show_social' ) || self::get_option( 'rx_footer_show_menu' ) ) {
						$this->render_middle_area();
					}

					if ( self::get_option( 'rx_footer_show_bottom_bar' ) ) {
						$this->render_bottom_bar();
					}
					?>

				</div>

				<?php do_action( 'rx_after_footer_inner' ); ?>
			</footer>
			<?php
		}

		/**
		 * Container class.
		 *
		 * @return string
		 */
		private function container_class() {
			$width = self::get_option( 'rx_footer_width' );

			if ( 'full' === $width ) {
				return 'rx-footer-container rx-footer-container-full';
			}

			return 'rx-footer-container rx-footer-container-contained';
		}

		/**
		 * Footer inline style.
		 *
		 * @return string
		 */
		private function footer_inline_style() {
			$bg     = sanitize_hex_color( self::get_option( 'rx_footer_bg_color' ) );
			$text   = sanitize_hex_color( self::get_option( 'rx_footer_text_color' ) );
			$link   = sanitize_hex_color( self::get_option( 'rx_footer_link_color' ) );
			$hover  = sanitize_hex_color( self::get_option( 'rx_footer_link_hover_color' ) );
			$border = sanitize_hex_color( self::get_option( 'rx_footer_border_color' ) );

			$style = sprintf(
				'--rx-footer-bg:%1$s;--rx-footer-text:%2$s;--rx-footer-link:%3$s;--rx-footer-link-hover:%4$s;--rx-footer-border:%5$s;',
				esc_attr( $bg ),
				esc_attr( $text ),
				esc_attr( $link ),
				esc_attr( $hover ),
				esc_attr( $border )
			);

			return 'style="' . esc_attr( $style ) . '"';
		}

		/**
		 * Render CTA.
		 *
		 * @return void
		 */
		private function render_cta() {
			$title       = self::get_option( 'rx_footer_cta_title' );
			$text        = self::get_option( 'rx_footer_cta_text' );
			$button_text = self::get_option( 'rx_footer_cta_button_text' );
			$button_url  = self::get_option( 'rx_footer_cta_button_url' );

			if ( empty( $title ) && empty( $text ) && empty( $button_text ) ) {
				return;
			}

			?>
			<section class="rx-footer-cta" aria-label="<?php esc_attr_e( 'Footer call to action', 'rx-theme' ); ?>">
				<div class="rx-footer-cta-content">
					<?php if ( ! empty( $title ) ) : ?>
						<h2 class="rx-footer-cta-title"><?php echo esc_html( $title ); ?></h2>
					<?php endif; ?>

					<?php if ( ! empty( $text ) ) : ?>
						<div class="rx-footer-cta-text"><?php echo wp_kses_post( wpautop( $text ) ); ?></div>
					<?php endif; ?>
				</div>

				<?php if ( ! empty( $button_text ) && ! empty( $button_url ) ) : ?>
					<div class="rx-footer-cta-action">
						<a class="rx-footer-cta-button" href="<?php echo esc_url( $button_url ); ?>">
							<?php echo esc_html( $button_text ); ?>
						</a>
					</div>
				<?php endif; ?>
			</section>
			<?php
		}

		/**
		 * Render widget columns.
		 *
		 * @param int $columns Columns.
		 * @return void
		 */
		private function render_widget_columns( $columns ) {
			$has_widgets = false;

			for ( $i = 1; $i <= $columns; $i++ ) {
				if ( is_active_sidebar( 'rx-footer-' . $i ) ) {
					$has_widgets = true;
					break;
				}
			}

			if ( ! $has_widgets ) {
				return;
			}

			?>
			<div class="rx-footer-widgets rx-footer-widget-grid rx-footer-widget-grid-<?php echo esc_attr( $columns ); ?>">
				<?php for ( $i = 1; $i <= $columns; $i++ ) : ?>
					<?php if ( is_active_sidebar( 'rx-footer-' . $i ) ) : ?>
						<div class="rx-footer-column rx-footer-column-<?php echo esc_attr( $i ); ?>">
							<?php dynamic_sidebar( 'rx-footer-' . $i ); ?>
						</div>
					<?php endif; ?>
				<?php endfor; ?>
			</div>
			<?php
		}

		/**
		 * Render middle area.
		 *
		 * @return void
		 */
		private function render_middle_area() {
			?>
			<div class="rx-footer-middle">
				<?php
				if ( self::get_option( 'rx_footer_show_contact' ) ) {
					$this->render_contact_info();
				}

				if ( self::get_option( 'rx_footer_show_menu' ) ) {
					$this->render_footer_menu();
				}

				if ( self::get_option( 'rx_footer_show_social' ) ) {
					$this->render_social_links();
				}
				?>
			</div>
			<?php
		}

		/**
		 * Render contact info.
		 *
		 * @return void
		 */
		private function render_contact_info() {
			$title   = self::get_option( 'rx_footer_contact_title' );
			$address = self::get_option( 'rx_footer_contact_address' );
			$phone   = self::get_option( 'rx_footer_contact_phone' );
			$email   = self::get_option( 'rx_footer_contact_email' );

			if ( empty( $address ) && empty( $phone ) && empty( $email ) ) {
				return;
			}

			?>
			<section class="rx-footer-contact" aria-label="<?php esc_attr_e( 'Footer contact information', 'rx-theme' ); ?>">
				<?php if ( ! empty( $title ) ) : ?>
					<h2 class="rx-footer-section-title"><?php echo esc_html( $title ); ?></h2>
				<?php endif; ?>

				<ul class="rx-footer-contact-list">
					<?php if ( ! empty( $address ) ) : ?>
						<li class="rx-footer-contact-item rx-footer-contact-address">
							<span class="rx-footer-contact-icon" aria-hidden="true">📍</span>
							<span><?php echo wp_kses_post( nl2br( $address ) ); ?></span>
						</li>
					<?php endif; ?>

					<?php if ( ! empty( $phone ) ) : ?>
						<li class="rx-footer-contact-item rx-footer-contact-phone">
							<span class="rx-footer-contact-icon" aria-hidden="true"></span>
							<a href="tel:<?php echo esc_attr( preg_replace( '/[^0-9+]/', '', $phone ) ); ?>">
								<?php echo esc_html( $phone ); ?>
							</a>
						</li>
					<?php endif; ?>

					<?php if ( ! empty( $email ) && is_email( $email ) ) : ?>
						<li class="rx-footer-contact-item rx-footer-contact-email">
							<span class="rx-footer-contact-icon" aria-hidden="true"></span>
							<a href="mailto:<?php echo esc_attr( antispambot( $email ) ); ?>">
								<?php echo esc_html( antispambot( $email ) ); ?>
							</a>
						</li>
					<?php endif; ?>
				</ul>
			</section>
			<?php
		}

		/**
		 * Render footer menu.
		 *
		 * @return void
		 */
		private function render_footer_menu() {
			if ( ! has_nav_menu( 'rx_footer_menu' ) ) {
				return;
			}

			?>
			<nav class="rx-footer-navigation" aria-label="<?php esc_attr_e( 'Footer Menu', 'rx-theme' ); ?>">
				<?php
				wp_nav_menu(
					array(
						'theme_location' => 'rx_footer_menu',
						'menu_class'     => 'rx-footer-menu',
						'container'      => false,
						'depth'          => 2,
						'fallback_cb'    => false,
					)
				);
				?>
			</nav>
			<?php
		}

		/**
		 * Render social links.
		 *
		 * @return void
		 */
		private function render_social_links() {
			$socials = $this->social_platforms();
			$items   = array();

			foreach ( $socials as $key => $data ) {
				$url = self::get_option( $key );

				if ( ! empty( $url ) ) {
					$items[] = array(
						'name' => $data['name'],
						'url'  => esc_url( $url ),
					);
				}
			}

			if ( empty( $items ) ) {
				return;
			}

			?>
			<nav class="rx-footer-social" aria-label="<?php esc_attr_e( 'Social links', 'rx-theme' ); ?>">
				<ul class="rx-footer-social-list">
					<?php foreach ( $items as $item ) : ?>
						<li class="rx-footer-social-item">
							<a class="rx-footer-social-link"
								href="<?php echo esc_url( $item['url'] ); ?>"
								target="_blank"
								rel="noopener noreferrer"
								aria-label="<?php echo esc_attr( $item['name'] ); ?>">
								<span aria-hidden="true"><?php echo esc_html( $this->social_icon_text( $item['name'] ) ); ?></span>
								<span class="screen-reader-text"><?php echo esc_html( $item['name'] ); ?></span>
							</a>
						</li>
					<?php endforeach; ?>
				</ul>
			</nav>
			<?php
		}

		/**
		 * Simple text/icon for social platform.
		 *
		 * @param string $name Platform.
		 * @return string
		 */
		private function social_icon_text( $name ) {
			$icons = array(
				'Facebook'  => 'f',
				'X'         => '𝕏',
				'LinkedIn'  => 'in',
				'YouTube'   => '▶',
				'Instagram' => '◎',
				'Pinterest' => 'P',
				'GitHub'    => 'GH',
				'WhatsApp'  => 'WA',
				'Telegram'  => 'TG',
			);

			return isset( $icons[ $name ] ) ? $icons[ $name ] : $name;
		}

		/**
		 * Render bottom bar.
		 *
		 * @return void
		 */
		private function render_bottom_bar() {
			?>
			<div class="rx-footer-bottom">
				<div class="rx-footer-bottom-left">
					<?php $this->render_copyright(); ?>
				</div>

				<div class="rx-footer-bottom-right">
					<?php $this->render_bottom_menu(); ?>
					<?php $this->render_credit(); ?>
				</div>
			</div>
			<?php
		}

		/**
		 * Render copyright.
		 *
		 * @return void
		 */
		private function render_copyright() {
			$text = self::get_option( 'rx_footer_copyright' );

			if ( empty( $text ) ) {
				return;
			}

			$replace = array(
				'{year}'      => date_i18n( 'Y' ),
				'{site_name}' => get_bloginfo( 'name' ),
				'{site_url}'  => home_url( '/' ),
			);

			$text = strtr( $text, $replace );

			?>
			<div class="rx-footer-copyright">
				<?php echo wp_kses_post( $text ); ?>
			</div>
			<?php
		}

		/**
		 * Render bottom menu.
		 *
		 * @return void
		 */
		private function render_bottom_menu() {
			if ( has_nav_menu( 'rx_footer_bottom_menu' ) ) {
				wp_nav_menu(
					array(
						'theme_location' => 'rx_footer_bottom_menu',
						'menu_class'     => 'rx-footer-bottom-menu',
						'container'      => 'nav',
						'container_class'=> 'rx-footer-bottom-navigation',
						'depth'          => 1,
						'fallback_cb'    => false,
					)
				);
			}

			if ( has_nav_menu( 'rx_footer_legal_menu' ) ) {
				wp_nav_menu(
					array(
						'theme_location' => 'rx_footer_legal_menu',
						'menu_class'     => 'rx-footer-legal-menu',
						'container'      => 'nav',
						'container_class'=> 'rx-footer-legal-navigation',
						'depth'          => 1,
						'fallback_cb'    => false,
					)
				);
			}
		}

		/**
		 * Render credit.
		 *
		 * @return void
		 */
		private function render_credit() {
			if ( ! self::get_option( 'rx_footer_credit_enable' ) ) {
				return;
			}

			$credit = self::get_option( 'rx_footer_credit_text' );

			if ( empty( $credit ) ) {
				return;
			}

			?>
			<div class="rx-footer-credit">
				<?php echo esc_html( $credit ); ?>
			</div>
			<?php
		}

		/**
		 * Render back to top button.
		 *
		 * @return void
		 */
		public function render_back_to_top() {
			if ( ! self::get_option( 'rx_footer_show_back_to_top' ) ) {
				return;
			}

			?>
			<button type="button" class="rx-back-to-top" id="rxBackToTop" aria-label="<?php esc_attr_e( 'Back to top', 'rx-theme' ); ?>">
				<span aria-hidden="true"></span>
			</button>

			<script>
				(function () {
					'use strict';

					var button = document.getElementById('rxBackToTop');

					if (!button) {
						return;
					}

					function toggleButton() {
						if (window.scrollY > 350) {
							button.classList.add('is-visible');
						} else {
							button.classList.remove('is-visible');
						}
					}

					button.addEventListener('click', function () {
						window.scrollTo({
							top: 0,
							behavior: 'smooth'
						});
					});

					window.addEventListener('scroll', toggleButton, { passive: true });
					toggleButton();
				})();
			</script>
			<?php
		}

		/**
		 * Render schema.
		 *
		 * @return void
		 */
		public function render_footer_schema() {
			if ( ! self::get_option( 'rx_footer_schema_enable' ) ) {
				return;
			}

			$name = self::get_option( 'rx_footer_org_name' );
			$url  = self::get_option( 'rx_footer_org_url' );
			$logo = self::get_option( 'rx_footer_org_logo' );

			if ( empty( $name ) || empty( $url ) ) {
				return;
			}

			$same_as = array();
			$socials = $this->social_platforms();

			foreach ( $socials as $key => $data ) {
				$social_url = self::get_option( $key );

				if ( ! empty( $social_url ) ) {
					$same_as[] = esc_url_raw( $social_url );
				}
			}

			$schema = array(
				'@context' => 'https://schema.org',
				'@type'    => 'Organization',
				'name'     => wp_strip_all_tags( $name ),
				'url'      => esc_url_raw( $url ),
			);

			if ( ! empty( $logo ) ) {
				$schema['logo'] = esc_url_raw( $logo );
			}

			if ( ! empty( $same_as ) ) {
				$schema['sameAs'] = $same_as;
			}

			?>
			<script type="application/ld+json">
				<?php echo wp_json_encode( $schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT ); ?>
			</script>
			<?php
		}
	}

endif;

/**
 * Initialize footer builder.
 */
RX_Footer_Builder::instance();

/**
 * Helper function for footer output.
 *
 * You can call this in footer.php:
 * rx_theme_footer();
 *
 * @return void
 */
if ( ! function_exists( 'rx_theme_footer' ) ) {
	function rx_theme_footer() {
		do_action( 'rx_theme_footer' );
	}
}

/**
 * Optional footer CSS.
 *
 * Better: move this CSS into your main theme stylesheet.
 */
add_action(
	'wp_head',
	function () {
		?>
		<style id="rx-footer-builder-css">
			.rx-site-footer {
				background: var(--rx-footer-bg);
				color: var(--rx-footer-text);
				border-top: 1px solid var(--rx-footer-border);
				font-size: 16px;
				line-height: 1.7;
			}

			.rx-site-footer a {
				color: var(--rx-footer-link);
				text-decoration: none;
			}

			.rx-site-footer a:hover,
			.rx-site-footer a:focus {
				color: var(--rx-footer-link-hover);
				text-decoration: underline;
			}

			.rx-footer-container-contained {
				max-width: 1200px;
				margin: 0 auto;
				padding: 48px 20px 24px;
			}

			.rx-footer-container-full {
				width: 100%;
				padding: 48px 32px 24px;
			}

			.rx-footer-cta {
				display: flex;
				align-items: center;
				justify-content: space-between;
				gap: 24px;
				padding: 28px;
				margin-bottom: 40px;
				border: 1px solid var(--rx-footer-border);
				border-radius: 18px;
				background: rgba(255,255,255,0.04);
			}

			.rx-footer-cta-title {
				margin: 0 0 8px;
				font-size: 28px;
				line-height: 1.25;
			}

			.rx-footer-cta-text p {
				margin: 0;
			}

			.rx-footer-cta-button {
				display: inline-flex;
				align-items: center;
				justify-content: center;
				padding: 12px 22px;
				border-radius: 999px;
				background: var(--rx-footer-link-hover);
				color: #ffffff !important;
				font-weight: 700;
				text-decoration: none !important;
				white-space: nowrap;
			}

			.rx-footer-widget-grid {
				display: grid;
				gap: 32px;
				margin-bottom: 40px;
			}

			.rx-footer-widget-grid-1 {
				grid-template-columns: 1fr;
			}

			.rx-footer-widget-grid-2 {
				grid-template-columns: repeat(2, minmax(0, 1fr));
			}

			.rx-footer-widget-grid-3 {
				grid-template-columns: repeat(3, minmax(0, 1fr));
			}

			.rx-footer-widget-grid-4 {
				grid-template-columns: repeat(4, minmax(0, 1fr));
			}

			.rx-footer-widget-grid-5 {
				grid-template-columns: repeat(5, minmax(0, 1fr));
			}

			.rx-footer-widget-grid-6 {
				grid-template-columns: repeat(6, minmax(0, 1fr));
			}

			.rx-footer-widget-title,
			.rx-footer-section-title {
				margin: 0 0 16px;
				font-size: 18px;
				line-height: 1.3;
				color: var(--rx-footer-link);
			}

			.rx-footer-widget ul,
			.rx-footer-menu,
			.rx-footer-bottom-menu,
			.rx-footer-legal-menu,
			.rx-footer-contact-list,
			.rx-footer-social-list {
				margin: 0;
				padding: 0;
				list-style: none;
			}

			.rx-footer-widget li,
			.rx-footer-menu li,
			.rx-footer-bottom-menu li,
			.rx-footer-legal-menu li {
				margin-bottom: 8px;
			}

			.rx-footer-middle {
				display: grid;
				grid-template-columns: 1.2fr 1fr 1fr;
				gap: 32px;
				padding: 32px 0;
				border-top: 1px solid var(--rx-footer-border);
			}

			.rx-footer-contact-item {
				display: flex;
				gap: 10px;
				margin-bottom: 10px;
			}

			.rx-footer-contact-icon {
				flex: 0 0 auto;
			}

			.rx-footer-social-list {
				display: flex;
				flex-wrap: wrap;
				gap: 10px;
			}

			.rx-footer-social-link {
				display: inline-flex;
				width: 40px;
				height: 40px;
				align-items: center;
				justify-content: center;
				border-radius: 999px;
				border: 1px solid var(--rx-footer-border);
				background: rgba(255,255,255,0.04);
				font-weight: 700;
				text-decoration: none !important;
			}

			.rx-footer-bottom {
				display: flex;
				justify-content: space-between;
				align-items: center;
				gap: 20px;
				padding-top: 24px;
				border-top: 1px solid var(--rx-footer-border);
				font-size: 14px;
			}

			.rx-footer-bottom-right {
				display: flex;
				flex-wrap: wrap;
				align-items: center;
				justify-content: flex-end;
				gap: 16px;
			}

			.rx-footer-bottom-menu,
			.rx-footer-legal-menu {
				display: flex;
				flex-wrap: wrap;
				gap: 14px;
			}

			.rx-footer-bottom-menu li,
			.rx-footer-legal-menu li {
				margin-bottom: 0;
			}

			.rx-back-to-top {
				position: fixed;
				right: 20px;
				bottom: 20px;
				z-index: 999;
				width: 46px;
				height: 46px;
				border: 0;
				border-radius: 999px;
				background: #111827;
				color: #ffffff;
				cursor: pointer;
				opacity: 0;
				visibility: hidden;
				transform: translateY(12px);
				transition: opacity .2s ease, transform .2s ease, visibility .2s ease;
				box-shadow: 0 10px 24px rgba(0,0,0,.22);
			}

			.rx-back-to-top.is-visible {
				opacity: 1;
				visibility: visible;
				transform: translateY(0);
			}

			.rx-footer-layout-2 .rx-footer-middle,
			.rx-footer-layout-2 .rx-footer-bottom {
				text-align: center;
				justify-content: center;
			}

			.rx-footer-layout-2 .rx-footer-social-list,
			.rx-footer-layout-2 .rx-footer-bottom-right {
				justify-content: center;
			}

			.rx-footer-layout-3 .rx-footer-widgets,
			.rx-footer-layout-3 .rx-footer-middle {
				display: none;
			}

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

			@media (max-width: 1024px) {
				.rx-footer-widget-grid-4,
				.rx-footer-widget-grid-5,
				.rx-footer-widget-grid-6 {
					grid-template-columns: repeat(2, minmax(0, 1fr));
				}

				.rx-footer-middle {
					grid-template-columns: 1fr 1fr;
				}
			}

			@media (max-width: 700px) {
				.rx-footer-container-contained,
				.rx-footer-container-full {
					padding: 36px 16px 20px;
				}

				.rx-footer-cta,
				.rx-footer-bottom {
					flex-direction: column;
					align-items: flex-start;
				}

				.rx-footer-widget-grid,
				.rx-footer-widget-grid-2,
				.rx-footer-widget-grid-3,
				.rx-footer-widget-grid-4,
				.rx-footer-widget-grid-5,
				.rx-footer-widget-grid-6,
				.rx-footer-middle {
					grid-template-columns: 1fr;
				}

				.rx-footer-bottom-right {
					justify-content: flex-start;
				}
			}
		</style>
		<?php
	},
	20
);

Update your footer.php

Inside your theme footer.php, add this where the footer should show:

<?php
if ( function_exists( 'rx_theme_footer' ) ) {
	rx_theme_footer();
}
?>

<?php wp_footer(); ?>
</body>
</html>

What this file gives your RX theme

This one file adds:

  1. Footer Customizer panel
  2. Footer enable/disable option
  3. Footer layout switcher
  4. Contained/full-width footer
  5. 1 to 6 footer widget columns
  6. Footer widget areas
  7. Footer menu
  8. Footer bottom menu
  9. Footer legal menu
  10. Contact info section
  11. Social links section
  12. Footer CTA section
  13. Dynamic copyright text
  14. {year} shortcode support
  15. {site_name} shortcode support
  16. Back-to-top button
  17. Organization schema JSON-LD
  18. Responsive footer CSS
  19. Safe sanitization
  20. Accessibility labels
  21. Hook support with rx_before_footer_inner and rx_after_footer_inner

After adding it, go to:

WordPress Dashboard → Appearance → Customize → RX Footer Builder

and you will see the footer controls.

Leave a Reply

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