menus.php

advanced inc/core/menus.php file for your RX WordPress theme, including menu registration, fallbacks, accessible walkers, schema-friendly attributes, mobile support, breadcrumbs-style helpers, and admin/menu enhancements.

Below is an advanced menus.php file for your RX theme.

Place it here:

rx-theme/inc/core/menus.php

Then load it from functions.php:

require_once get_template_directory() . '/inc/core/menus.php';
<?php
/**
 * RX Theme Menus
 *
 * Advanced menu registration, accessible menu walkers, mobile menu helpers,
 * fallback menus, menu item classes, schema attributes, admin improvements,
 * and developer-friendly helper functions.
 *
 * @package RX_Theme
 */

defined( 'ABSPATH' ) || exit;

if ( ! defined( 'RX_THEME_VERSION' ) ) {
	define( 'RX_THEME_VERSION', '1.0.0' );
}

if ( ! defined( 'RX_THEME_TEXT_DOMAIN' ) ) {
	define( 'RX_THEME_TEXT_DOMAIN', 'rx-theme' );
}

/**
 * ------------------------------------------------------------
 * 1. Register Theme Menu Locations
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_register_menus' ) ) {

	/**
	 * Register all menu locations for RX Theme.
	 */
	function rx_theme_register_menus() {

		register_nav_menus(
			array(
				'primary'          => esc_html__( 'Primary Menu', RX_THEME_TEXT_DOMAIN ),
				'secondary'        => esc_html__( 'Secondary Menu', RX_THEME_TEXT_DOMAIN ),
				'topbar'           => esc_html__( 'Top Bar Menu', RX_THEME_TEXT_DOMAIN ),
				'footer'           => esc_html__( 'Footer Menu', RX_THEME_TEXT_DOMAIN ),
				'footer_one'       => esc_html__( 'Footer Column One Menu', RX_THEME_TEXT_DOMAIN ),
				'footer_two'       => esc_html__( 'Footer Column Two Menu', RX_THEME_TEXT_DOMAIN ),
				'footer_three'     => esc_html__( 'Footer Column Three Menu', RX_THEME_TEXT_DOMAIN ),
				'footer_four'      => esc_html__( 'Footer Column Four Menu', RX_THEME_TEXT_DOMAIN ),
				'mobile'           => esc_html__( 'Mobile Menu', RX_THEME_TEXT_DOMAIN ),
				'offcanvas'        => esc_html__( 'Off Canvas Menu', RX_THEME_TEXT_DOMAIN ),
				'social'           => esc_html__( 'Social Links Menu', RX_THEME_TEXT_DOMAIN ),
				'utility'          => esc_html__( 'Utility Menu', RX_THEME_TEXT_DOMAIN ),
				'account'          => esc_html__( 'Account Menu', RX_THEME_TEXT_DOMAIN ),
				'dashboard'        => esc_html__( 'Dashboard Menu', RX_THEME_TEXT_DOMAIN ),
				'legal'            => esc_html__( 'Legal Menu', RX_THEME_TEXT_DOMAIN ),
				'category_menu'    => esc_html__( 'Category Menu', RX_THEME_TEXT_DOMAIN ),
				'mega_menu'        => esc_html__( 'Mega Menu', RX_THEME_TEXT_DOMAIN ),
				'doctor_menu'      => esc_html__( 'Doctor / Medical Menu', RX_THEME_TEXT_DOMAIN ),
				'blog_menu'        => esc_html__( 'Blog Menu', RX_THEME_TEXT_DOMAIN ),
				'sticky_header'    => esc_html__( 'Sticky Header Menu', RX_THEME_TEXT_DOMAIN ),
				'amp_menu'         => esc_html__( 'AMP / Lightweight Menu', RX_THEME_TEXT_DOMAIN ),
			)
		);
	}
}
add_action( 'after_setup_theme', 'rx_theme_register_menus' );


/**
 * ------------------------------------------------------------
 * 2. Menu Support Helpers
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_has_menu' ) ) {

	/**
	 * Check whether a menu location has assigned menu.
	 *
	 * @param string $location Menu location.
	 * @return bool
	 */
	function rx_theme_has_menu( $location = 'primary' ) {
		return has_nav_menu( sanitize_key( $location ) );
	}
}


if ( ! function_exists( 'rx_theme_get_menu_locations' ) ) {

	/**
	 * Get all registered menu locations.
	 *
	 * @return array
	 */
	function rx_theme_get_menu_locations() {
		$locations = get_registered_nav_menus();

		if ( ! is_array( $locations ) ) {
			return array();
		}

		return $locations;
	}
}


if ( ! function_exists( 'rx_theme_get_assigned_menu_object' ) ) {

	/**
	 * Get assigned menu object by location.
	 *
	 * @param string $location Menu location.
	 * @return WP_Term|false
	 */
	function rx_theme_get_assigned_menu_object( $location = 'primary' ) {

		$location  = sanitize_key( $location );
		$locations = get_nav_menu_locations();

		if ( empty( $locations[ $location ] ) ) {
			return false;
		}

		return wp_get_nav_menu_object( absint( $locations[ $location ] ) );
	}
}


if ( ! function_exists( 'rx_theme_get_menu_items_by_location' ) ) {

	/**
	 * Get menu items by menu location.
	 *
	 * @param string $location Menu location.
	 * @return array
	 */
	function rx_theme_get_menu_items_by_location( $location = 'primary' ) {

		$menu = rx_theme_get_assigned_menu_object( $location );

		if ( ! $menu || is_wp_error( $menu ) ) {
			return array();
		}

		$items = wp_get_nav_menu_items( $menu->term_id );

		if ( empty( $items ) || ! is_array( $items ) ) {
			return array();
		}

		return $items;
	}
}


/**
 * ------------------------------------------------------------
 * 3. Accessible Advanced Walker
 * ------------------------------------------------------------
 */

if ( ! class_exists( 'RX_Theme_Accessible_Menu_Walker' ) ) {

	/**
	 * Accessible frontend menu walker.
	 */
	class RX_Theme_Accessible_Menu_Walker extends Walker_Nav_Menu {

		/**
		 * Current menu location.
		 *
		 * @var string
		 */
		protected $location = '';

		/**
		 * Constructor.
		 *
		 * @param string $location Menu location.
		 */
		public function __construct( $location = 'primary' ) {
			$this->location = sanitize_key( $location );
		}

		/**
		 * Start submenu level.
		 *
		 * @param string $output Output HTML.
		 * @param int    $depth Depth.
		 * @param object $args Args.
		 */
		public function start_lvl( &$output, $depth = 0, $args = null ) {
			$indent = str_repeat( "\t", $depth );

			$classes = array(
				'rx-sub-menu',
				'rx-sub-menu-depth-' . absint( $depth + 1 ),
			);

			$class_names = implode( ' ', array_map( 'sanitize_html_class', $classes ) );

			$output .= "\n{$indent}<ul class=\"" . esc_attr( $class_names ) . "\" role=\"menu\">\n";
		}

		/**
		 * End submenu level.
		 *
		 * @param string $output Output HTML.
		 * @param int    $depth Depth.
		 * @param object $args Args.
		 */
		public function end_lvl( &$output, $depth = 0, $args = null ) {
			$indent  = str_repeat( "\t", $depth );
			$output .= "{$indent}</ul>\n";
		}

		/**
		 * Start menu item.
		 *
		 * @param string $output Output HTML.
		 * @param object $item Menu item.
		 * @param int    $depth Depth.
		 * @param object $args Args.
		 * @param int    $id Item ID.
		 */
		public function start_el( &$output, $item, $depth = 0, $args = null, $id = 0 ) {

			$indent = $depth ? str_repeat( "\t", $depth ) : '';

			$classes = empty( $item->classes ) ? array() : (array) $item->classes;

			$classes[] = 'rx-menu-item';
			$classes[] = 'rx-menu-item-' . absint( $item->ID );
			$classes[] = 'rx-menu-depth-' . absint( $depth );

			if ( in_array( 'menu-item-has-children', $classes, true ) ) {
				$classes[] = 'rx-menu-item-has-children';
			}

			if ( $this->location ) {
				$classes[] = 'rx-menu-location-' . sanitize_html_class( $this->location );
			}

			if ( ! empty( $item->current ) ) {
				$classes[] = 'rx-current-menu-item';
			}

			if ( ! empty( $item->current_item_parent ) ) {
				$classes[] = 'rx-current-menu-parent';
			}

			if ( ! empty( $item->current_item_ancestor ) ) {
				$classes[] = 'rx-current-menu-ancestor';
			}

			$classes = apply_filters( 'rx_theme_menu_item_classes', $classes, $item, $args, $depth, $this->location );

			$class_names = implode( ' ', array_filter( array_map( 'sanitize_html_class', $classes ) ) );

			$item_id = 'rx-menu-item-' . absint( $item->ID );

			$item_attrs = array(
				'id'         => $item_id,
				'class'      => $class_names,
				'role'       => 'none',
				'itemscope'  => 'itemscope',
				'itemtype'   => 'https://www.schema.org/SiteNavigationElement',
			);

			$item_attrs = apply_filters( 'rx_theme_menu_item_attributes', $item_attrs, $item, $args, $depth, $this->location );

			$output .= $indent . '<li' . rx_theme_build_html_attributes( $item_attrs ) . '>';

			$atts = array();

			$atts['title'] = ! empty( $item->attr_title ) ? $item->attr_title : '';
			$atts['target'] = ! empty( $item->target ) ? $item->target : '';
			$atts['rel'] = ! empty( $item->xfn ) ? $item->xfn : '';
			$atts['href'] = ! empty( $item->url ) ? $item->url : '';
			$atts['class'] = 'rx-menu-link rx-menu-link-depth-' . absint( $depth );
			$atts['role'] = 'menuitem';
			$atts['itemprop'] = 'url';

			if ( in_array( 'menu-item-has-children', $classes, true ) ) {
				$atts['aria-haspopup'] = 'true';
				$atts['aria-expanded'] = 'false';
			}

			if ( ! empty( $item->current ) ) {
				$atts['aria-current'] = 'page';
			}

			if ( '_blank' === $atts['target'] ) {
				$rel_parts = array_filter( explode( ' ', $atts['rel'] ) );

				if ( ! in_array( 'noopener', $rel_parts, true ) ) {
					$rel_parts[] = 'noopener';
				}

				if ( ! in_array( 'noreferrer', $rel_parts, true ) ) {
					$rel_parts[] = 'noreferrer';
				}

				$atts['rel'] = implode( ' ', array_unique( $rel_parts ) );
			}

			$atts = apply_filters( 'rx_theme_menu_link_attributes', $atts, $item, $args, $depth, $this->location );

			$title = apply_filters( 'the_title', $item->title, $item->ID );
			$title = apply_filters( 'nav_menu_item_title', $title, $item, $args, $depth );

			$icon_html = rx_theme_get_menu_icon_html( $item );

			$item_output  = isset( $args->before ) ? $args->before : '';
			$item_output .= '<a' . rx_theme_build_html_attributes( $atts ) . '>';
			$item_output .= isset( $args->link_before ) ? $args->link_before : '';
			$item_output .= $icon_html;
			$item_output .= '<span class="rx-menu-text" itemprop="name">' . esc_html( $title ) . '</span>';

			if ( in_array( 'menu-item-has-children', $classes, true ) ) {
				$item_output .= '<span class="rx-submenu-indicator" aria-hidden="true">▾</span>';
			}

			$item_output .= isset( $args->link_after ) ? $args->link_after : '';
			$item_output .= '</a>';

			if ( in_array( 'menu-item-has-children', $classes, true ) ) {
				$item_output .= '<button class="rx-submenu-toggle" type="button" aria-expanded="false" aria-label="' . esc_attr__( 'Open submenu', RX_THEME_TEXT_DOMAIN ) . '">';
				$item_output .= '<span aria-hidden="true">+</span>';
				$item_output .= '</button>';
			}

			$item_output .= isset( $args->after ) ? $args->after : '';

			$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
		}

		/**
		 * End menu item.
		 *
		 * @param string $output Output HTML.
		 * @param object $item Menu item.
		 * @param int    $depth Depth.
		 * @param object $args Args.
		 */
		public function end_el( &$output, $item, $depth = 0, $args = null ) {
			$output .= "</li>\n";
		}
	}
}


/**
 * ------------------------------------------------------------
 * 4. HTML Attribute Builder
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_build_html_attributes' ) ) {

	/**
	 * Build safe HTML attributes.
	 *
	 * @param array $attributes Attributes.
	 * @return string
	 */
	function rx_theme_build_html_attributes( $attributes = array() ) {

		if ( empty( $attributes ) || ! is_array( $attributes ) ) {
			return '';
		}

		$output = '';

		foreach ( $attributes as $name => $value ) {
			if ( '' === $value || null === $value || false === $value ) {
				continue;
			}

			$name = sanitize_key( $name );

			if ( true === $value ) {
				$output .= ' ' . esc_attr( $name );
				continue;
			}

			if ( 'href' === $name ) {
				$output .= ' href="' . esc_url( $value ) . '"';
				continue;
			}

			if ( 'itemtype' === $name ) {
				$output .= ' itemtype="' . esc_url( $value ) . '"';
				continue;
			}

			$output .= ' ' . esc_attr( $name ) . '="' . esc_attr( $value ) . '"';
		}

		return $output;
	}
}


/**
 * ------------------------------------------------------------
 * 5. Menu Icon Support
 * ------------------------------------------------------------
 *
 * You can add custom CSS classes to menu items from:
 * Appearance > Menus > CSS Classes
 *
 * Example:
 * rx-icon-home
 * rx-icon-user
 * rx-icon-search
 */

if ( ! function_exists( 'rx_theme_get_menu_icon_html' ) ) {

	/**
	 * Get menu icon HTML from menu item classes.
	 *
	 * @param object $item Menu item.
	 * @return string
	 */
	function rx_theme_get_menu_icon_html( $item ) {

		if ( empty( $item->classes ) || ! is_array( $item->classes ) ) {
			return '';
		}

		$icon_class = '';

		foreach ( $item->classes as $class ) {
			if ( 0 === strpos( $class, 'rx-icon-' ) ) {
				$icon_class = sanitize_html_class( $class );
				break;
			}
		}

		if ( empty( $icon_class ) ) {
			return '';
		}

		return '<span class="rx-menu-icon ' . esc_attr( $icon_class ) . '" aria-hidden="true"></span>';
	}
}


/**
 * ------------------------------------------------------------
 * 6. Main Render Function
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_render_menu' ) ) {

	/**
	 * Render a menu by location.
	 *
	 * @param string $location Menu location.
	 * @param array  $args Custom args.
	 * @return void
	 */
	function rx_theme_render_menu( $location = 'primary', $args = array() ) {

		$location = sanitize_key( $location );

		$defaults = array(
			'theme_location'  => $location,
			'menu_id'         => 'rx-' . $location . '-menu',
			'menu_class'      => 'rx-menu rx-' . $location . '-menu',
			'container'       => 'nav',
			'container_id'    => 'rx-' . $location . '-navigation',
			'container_class' => 'rx-navigation rx-' . $location . '-navigation',
			'fallback_cb'     => 'rx_theme_menu_fallback',
			'depth'           => 4,
			'walker'          => new RX_Theme_Accessible_Menu_Walker( $location ),
			'items_wrap'      => '<ul id="%1$s" class="%2$s" role="menubar" itemscope itemtype="https://www.schema.org/SiteNavigationElement">%3$s</ul>',
			'echo'            => true,
		);

		$args = wp_parse_args( $args, $defaults );

		$args = apply_filters( 'rx_theme_render_menu_args', $args, $location );

		wp_nav_menu( $args );
	}
}


/**
 * ------------------------------------------------------------
 * 7. Menu Fallback
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_menu_fallback' ) ) {

	/**
	 * Fallback menu when no menu is assigned.
	 *
	 * @param array $args wp_nav_menu args.
	 * @return void|string
	 */
	function rx_theme_menu_fallback( $args = array() ) {

		$location = isset( $args['theme_location'] ) ? sanitize_key( $args['theme_location'] ) : 'primary';

		$menu_id    = ! empty( $args['menu_id'] ) ? sanitize_html_class( $args['menu_id'] ) : 'rx-fallback-menu';
		$menu_class = ! empty( $args['menu_class'] ) ? esc_attr( $args['menu_class'] ) : 'rx-menu rx-fallback-menu';

		$home_url = home_url( '/' );

		$output  = '<nav class="rx-navigation rx-fallback-navigation rx-' . esc_attr( $location ) . '-fallback-navigation" aria-label="' . esc_attr__( 'Fallback Menu', RX_THEME_TEXT_DOMAIN ) . '">';
		$output .= '<ul id="' . esc_attr( $menu_id ) . '" class="' . esc_attr( $menu_class ) . '" role="menubar">';
		$output .= '<li class="rx-menu-item rx-fallback-menu-item" role="none">';
		$output .= '<a class="rx-menu-link" role="menuitem" href="' . esc_url( $home_url ) . '">' . esc_html__( 'Home', RX_THEME_TEXT_DOMAIN ) . '</a>';
		$output .= '</li>';

		$pages = get_pages(
			array(
				'sort_column' => 'menu_order,post_title',
				'number'      => 6,
				'post_status' => 'publish',
			)
		);

		if ( ! empty( $pages ) ) {
			foreach ( $pages as $page ) {
				$output .= '<li class="rx-menu-item rx-fallback-menu-item" role="none">';
				$output .= '<a class="rx-menu-link" role="menuitem" href="' . esc_url( get_permalink( $page->ID ) ) . '">' . esc_html( get_the_title( $page->ID ) ) . '</a>';
				$output .= '</li>';
			}
		}

		if ( current_user_can( 'edit_theme_options' ) ) {
			$output .= '<li class="rx-menu-item rx-fallback-menu-item rx-menu-admin-link" role="none">';
			$output .= '<a class="rx-menu-link" role="menuitem" href="' . esc_url( admin_url( 'nav-menus.php' ) ) . '">' . esc_html__( 'Create Menu', RX_THEME_TEXT_DOMAIN ) . '</a>';
			$output .= '</li>';
		}

		$output .= '</ul>';
		$output .= '</nav>';

		if ( isset( $args['echo'] ) && false === $args['echo'] ) {
			return $output;
		}

		echo wp_kses_post( $output );
	}
}


/**
 * ------------------------------------------------------------
 * 8. Primary Menu Helper
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_primary_menu' ) ) {

	/**
	 * Display primary menu.
	 *
	 * @param array $args Args.
	 */
	function rx_theme_primary_menu( $args = array() ) {
		rx_theme_render_menu( 'primary', $args );
	}
}


/**
 * ------------------------------------------------------------
 * 9. Mobile Menu Helper
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_mobile_menu' ) ) {

	/**
	 * Display mobile menu.
	 *
	 * @param array $args Args.
	 */
	function rx_theme_mobile_menu( $args = array() ) {

		$location = has_nav_menu( 'mobile' ) ? 'mobile' : 'primary';

		$defaults = array(
			'menu_id'         => 'rx-mobile-menu',
			'menu_class'      => 'rx-menu rx-mobile-menu',
			'container_id'    => 'rx-mobile-navigation',
			'container_class' => 'rx-navigation rx-mobile-navigation',
			'depth'           => 4,
		);

		rx_theme_render_menu( $location, wp_parse_args( $args, $defaults ) );
	}
}


/**
 * ------------------------------------------------------------
 * 10. Footer Menu Helper
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_footer_menu' ) ) {

	/**
	 * Display footer menu.
	 *
	 * @param array $args Args.
	 */
	function rx_theme_footer_menu( $args = array() ) {

		$defaults = array(
			'menu_id'         => 'rx-footer-menu',
			'menu_class'      => 'rx-menu rx-footer-menu',
			'container_id'    => 'rx-footer-navigation',
			'container_class' => 'rx-navigation rx-footer-navigation',
			'depth'           => 2,
		);

		rx_theme_render_menu( 'footer', wp_parse_args( $args, $defaults ) );
	}
}


/**
 * ------------------------------------------------------------
 * 11. Social Menu Walker
 * ------------------------------------------------------------
 */

if ( ! class_exists( 'RX_Theme_Social_Menu_Walker' ) ) {

	/**
	 * Social links menu walker.
	 */
	class RX_Theme_Social_Menu_Walker extends Walker_Nav_Menu {

		/**
		 * Start menu item.
		 *
		 * @param string $output Output.
		 * @param object $item Item.
		 * @param int    $depth Depth.
		 * @param object $args Args.
		 * @param int    $id ID.
		 */
		public function start_el( &$output, $item, $depth = 0, $args = null, $id = 0 ) {

			$classes = empty( $item->classes ) ? array() : (array) $item->classes;

			$classes[] = 'rx-social-menu-item';

			$network = rx_theme_detect_social_network( $item->url );

			if ( $network ) {
				$classes[] = 'rx-social-' . sanitize_html_class( $network );
			}

			$class_names = implode( ' ', array_filter( array_map( 'sanitize_html_class', $classes ) ) );

			$title = apply_filters( 'the_title', $item->title, $item->ID );

			$atts = array(
				'href'        => $item->url,
				'class'       => 'rx-social-link',
				'target'      => '_blank',
				'rel'         => 'noopener noreferrer nofollow',
				'aria-label'  => $title,
			);

			$output .= '<li class="' . esc_attr( $class_names ) . '">';
			$output .= '<a' . rx_theme_build_html_attributes( $atts ) . '>';
			$output .= '<span class="rx-social-icon rx-social-icon-' . esc_attr( $network ? $network : 'link' ) . '" aria-hidden="true"></span>';
			$output .= '<span class="screen-reader-text">' . esc_html( $title ) . '</span>';
			$output .= '</a>';
		}

		/**
		 * End menu item.
		 *
		 * @param string $output Output.
		 * @param object $item Item.
		 * @param int    $depth Depth.
		 * @param object $args Args.
		 */
		public function end_el( &$output, $item, $depth = 0, $args = null ) {
			$output .= '</li>';
		}
	}
}


if ( ! function_exists( 'rx_theme_social_menu' ) ) {

	/**
	 * Display social links menu.
	 *
	 * @param array $args Args.
	 */
	function rx_theme_social_menu( $args = array() ) {

		$defaults = array(
			'theme_location'  => 'social',
			'menu_id'         => 'rx-social-menu',
			'menu_class'      => 'rx-social-menu',
			'container'       => 'nav',
			'container_id'    => 'rx-social-navigation',
			'container_class' => 'rx-social-navigation',
			'depth'           => 1,
			'fallback_cb'     => false,
			'walker'          => new RX_Theme_Social_Menu_Walker(),
			'items_wrap'      => '<ul id="%1$s" class="%2$s">%3$s</ul>',
		);

		wp_nav_menu( wp_parse_args( $args, $defaults ) );
	}
}


if ( ! function_exists( 'rx_theme_detect_social_network' ) ) {

	/**
	 * Detect social network from URL.
	 *
	 * @param string $url URL.
	 * @return string
	 */
	function rx_theme_detect_social_network( $url ) {

		$url = strtolower( esc_url_raw( $url ) );

		$networks = array(
			'facebook'  => 'facebook.com',
			'twitter'   => 'twitter.com',
			'x'         => 'x.com',
			'linkedin'  => 'linkedin.com',
			'instagram' => 'instagram.com',
			'youtube'   => 'youtube.com',
			'tiktok'    => 'tiktok.com',
			'pinterest' => 'pinterest.com',
			'github'    => 'github.com',
			'telegram'  => 't.me',
			'whatsapp'  => 'wa.me',
			'reddit'    => 'reddit.com',
			'medium'    => 'medium.com',
			'threads'   => 'threads.net',
			'dribbble'  => 'dribbble.com',
			'behance'   => 'behance.net',
			'rss'       => '/feed',
		);

		foreach ( $networks as $name => $needle ) {
			if ( false !== strpos( $url, $needle ) ) {
				return $name;
			}
		}

		if ( 0 === strpos( $url, 'mailto:' ) ) {
			return 'email';
		}

		if ( 0 === strpos( $url, 'tel:' ) ) {
			return 'phone';
		}

		return 'link';
	}
}


/**
 * ------------------------------------------------------------
 * 12. Add Body Classes Based On Menus
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_menu_body_classes' ) ) {

	/**
	 * Add menu-related body classes.
	 *
	 * @param array $classes Body classes.
	 * @return array
	 */
	function rx_theme_menu_body_classes( $classes ) {

		$locations = array_keys( rx_theme_get_menu_locations() );

		foreach ( $locations as $location ) {
			if ( has_nav_menu( $location ) ) {
				$classes[] = 'rx-has-' . sanitize_html_class( $location ) . '-menu';
			} else {
				$classes[] = 'rx-no-' . sanitize_html_class( $location ) . '-menu';
			}
		}

		return $classes;
	}
}
add_filter( 'body_class', 'rx_theme_menu_body_classes' );


/**
 * ------------------------------------------------------------
 * 13. Add Menu Item Classes
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_nav_menu_css_class' ) ) {

	/**
	 * Add useful CSS classes to menu items.
	 *
	 * @param array  $classes Classes.
	 * @param object $item Menu item.
	 * @param object $args Args.
	 * @param int    $depth Depth.
	 * @return array
	 */
	function rx_theme_nav_menu_css_class( $classes, $item, $args, $depth ) {

		$classes[] = 'rx-nav-item';
		$classes[] = 'rx-nav-item-depth-' . absint( $depth );

		if ( ! empty( $args->theme_location ) ) {
			$classes[] = 'rx-nav-item-location-' . sanitize_html_class( $args->theme_location );
		}

		if ( ! empty( $item->object ) ) {
			$classes[] = 'rx-nav-object-' . sanitize_html_class( $item->object );
		}

		if ( ! empty( $item->type ) ) {
			$classes[] = 'rx-nav-type-' . sanitize_html_class( $item->type );
		}

		if ( ! empty( $item->menu_order ) ) {
			$classes[] = 'rx-nav-order-' . absint( $item->menu_order );
		}

		return array_unique( array_filter( $classes ) );
	}
}
add_filter( 'nav_menu_css_class', 'rx_theme_nav_menu_css_class', 10, 4 );


if ( ! function_exists( 'rx_theme_nav_menu_link_attributes_filter' ) ) {

	/**
	 * Add useful link attributes to menu anchors.
	 *
	 * @param array  $atts Attributes.
	 * @param object $item Menu item.
	 * @param object $args Args.
	 * @param int    $depth Depth.
	 * @return array
	 */
	function rx_theme_nav_menu_link_attributes_filter( $atts, $item, $args, $depth ) {

		$atts['data-rx-menu-depth'] = absint( $depth );

		if ( ! empty( $args->theme_location ) ) {
			$atts['data-rx-menu-location'] = sanitize_key( $args->theme_location );
		}

		if ( ! empty( $item->ID ) ) {
			$atts['data-rx-menu-item-id'] = absint( $item->ID );
		}

		if ( ! empty( $item->current ) ) {
			$atts['aria-current'] = 'page';
		}

		if ( ! empty( $atts['target'] ) && '_blank' === $atts['target'] ) {
			$rel = ! empty( $atts['rel'] ) ? $atts['rel'] : '';

			$rel_parts = array_filter( explode( ' ', $rel ) );

			$rel_parts[] = 'noopener';
			$rel_parts[] = 'noreferrer';

			$atts['rel'] = implode( ' ', array_unique( $rel_parts ) );
		}

		return $atts;
	}
}
add_filter( 'nav_menu_link_attributes', 'rx_theme_nav_menu_link_attributes_filter', 10, 4 );


/**
 * ------------------------------------------------------------
 * 14. Menu Container Attributes
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_nav_menu_container_aria_label' ) ) {

	/**
	 * Add aria label to nav containers.
	 *
	 * @param array $args Args.
	 * @return array
	 */
	function rx_theme_nav_menu_container_aria_label( $args ) {

		if ( empty( $args['container'] ) || 'nav' !== $args['container'] ) {
			return $args;
		}

		if ( empty( $args['container_aria_label'] ) ) {
			$location = ! empty( $args['theme_location'] ) ? $args['theme_location'] : 'menu';
			$args['container_aria_label'] = ucwords( str_replace( array( '-', '_' ), ' ', $location ) );
		}

		return $args;
	}
}
add_filter( 'wp_nav_menu_args', 'rx_theme_nav_menu_container_aria_label' );


/**
 * ------------------------------------------------------------
 * 15. Admin Menu Item Custom Fields
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_setup_menu_admin_fields' ) ) {

	/**
	 * Load admin menu custom field actions.
	 */
	function rx_theme_setup_menu_admin_fields() {
		add_action( 'wp_nav_menu_item_custom_fields', 'rx_theme_menu_item_custom_fields', 10, 5 );
		add_action( 'wp_update_nav_menu_item', 'rx_theme_save_menu_item_custom_fields', 10, 3 );
	}
}
add_action( 'admin_init', 'rx_theme_setup_menu_admin_fields' );


if ( ! function_exists( 'rx_theme_menu_item_custom_fields' ) ) {

	/**
	 * Add custom fields to menu item admin.
	 *
	 * @param int    $item_id Item ID.
	 * @param object $item Menu item.
	 * @param int    $depth Depth.
	 * @param object $args Args.
	 * @param int    $id Nav menu ID.
	 */
	function rx_theme_menu_item_custom_fields( $item_id, $item, $depth, $args, $id ) {

		$badge       = get_post_meta( $item_id, '_rx_menu_badge', true );
		$description = get_post_meta( $item_id, '_rx_menu_description', true );
		$mega        = get_post_meta( $item_id, '_rx_menu_mega', true );
		$highlight   = get_post_meta( $item_id, '_rx_menu_highlight', true );
		?>
		<p class="field-rx-menu-badge description description-wide">
			<label for="edit-menu-item-rx-badge-<?php echo esc_attr( $item_id ); ?>">
				<?php esc_html_e( 'RX Badge Text', RX_THEME_TEXT_DOMAIN ); ?><br>
				<input type="text"
					id="edit-menu-item-rx-badge-<?php echo esc_attr( $item_id ); ?>"
					class="widefat code edit-menu-item-rx-badge"
					name="menu-item-rx-badge[<?php echo esc_attr( $item_id ); ?>]"
					value="<?php echo esc_attr( $badge ); ?>">
			</label>
		</p>

		<p class="field-rx-menu-description description description-wide">
			<label for="edit-menu-item-rx-description-<?php echo esc_attr( $item_id ); ?>">
				<?php esc_html_e( 'RX Short Description', RX_THEME_TEXT_DOMAIN ); ?><br>
				<textarea id="edit-menu-item-rx-description-<?php echo esc_attr( $item_id ); ?>"
					class="widefat edit-menu-item-rx-description"
					rows="2"
					name="menu-item-rx-description[<?php echo esc_attr( $item_id ); ?>]"><?php echo esc_textarea( $description ); ?></textarea>
			</label>
		</p>

		<p class="field-rx-menu-mega description description-wide">
			<label>
				<input type="checkbox"
					name="menu-item-rx-mega[<?php echo esc_attr( $item_id ); ?>]"
					value="1" <?php checked( $mega, '1' ); ?>>
				<?php esc_html_e( 'Enable Mega Menu for this item', RX_THEME_TEXT_DOMAIN ); ?>
			</label>
		</p>

		<p class="field-rx-menu-highlight description description-wide">
			<label>
				<input type="checkbox"
					name="menu-item-rx-highlight[<?php echo esc_attr( $item_id ); ?>]"
					value="1" <?php checked( $highlight, '1' ); ?>>
				<?php esc_html_e( 'Highlight this menu item', RX_THEME_TEXT_DOMAIN ); ?>
			</label>
		</p>
		<?php
	}
}


if ( ! function_exists( 'rx_theme_save_menu_item_custom_fields' ) ) {

	/**
	 * Save menu item custom fields.
	 *
	 * @param int $menu_id Menu ID.
	 * @param int $menu_item_db_id Menu item DB ID.
	 * @param array $args Args.
	 */
	function rx_theme_save_menu_item_custom_fields( $menu_id, $menu_item_db_id, $args ) {

		if ( isset( $_POST['menu-item-rx-badge'][ $menu_item_db_id ] ) ) {
			update_post_meta(
				$menu_item_db_id,
				'_rx_menu_badge',
				sanitize_text_field( wp_unslash( $_POST['menu-item-rx-badge'][ $menu_item_db_id ] ) )
			);
		} else {
			delete_post_meta( $menu_item_db_id, '_rx_menu_badge' );
		}

		if ( isset( $_POST['menu-item-rx-description'][ $menu_item_db_id ] ) ) {
			update_post_meta(
				$menu_item_db_id,
				'_rx_menu_description',
				sanitize_textarea_field( wp_unslash( $_POST['menu-item-rx-description'][ $menu_item_db_id ] ) )
			);
		} else {
			delete_post_meta( $menu_item_db_id, '_rx_menu_description' );
		}

		if ( isset( $_POST['menu-item-rx-mega'][ $menu_item_db_id ] ) ) {
			update_post_meta( $menu_item_db_id, '_rx_menu_mega', '1' );
		} else {
			delete_post_meta( $menu_item_db_id, '_rx_menu_mega' );
		}

		if ( isset( $_POST['menu-item-rx-highlight'][ $menu_item_db_id ] ) ) {
			update_post_meta( $menu_item_db_id, '_rx_menu_highlight', '1' );
		} else {
			delete_post_meta( $menu_item_db_id, '_rx_menu_highlight' );
		}
	}
}


/**
 * ------------------------------------------------------------
 * 16. Add Custom Field Classes To Frontend Menu Items
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_custom_menu_field_classes' ) ) {

	/**
	 * Add classes based on custom menu item fields.
	 *
	 * @param array  $classes Classes.
	 * @param object $item Item.
	 * @param object $args Args.
	 * @param int    $depth Depth.
	 * @return array
	 */
	function rx_theme_custom_menu_field_classes( $classes, $item, $args, $depth ) {

		if ( get_post_meta( $item->ID, '_rx_menu_mega', true ) ) {
			$classes[] = 'rx-menu-mega-enabled';
		}

		if ( get_post_meta( $item->ID, '_rx_menu_highlight', true ) ) {
			$classes[] = 'rx-menu-highlight';
		}

		if ( get_post_meta( $item->ID, '_rx_menu_badge', true ) ) {
			$classes[] = 'rx-menu-has-badge';
		}

		if ( get_post_meta( $item->ID, '_rx_menu_description', true ) ) {
			$classes[] = 'rx-menu-has-description';
		}

		return $classes;
	}
}
add_filter( 'nav_menu_css_class', 'rx_theme_custom_menu_field_classes', 20, 4 );


if ( ! function_exists( 'rx_theme_menu_title_with_badge_description' ) ) {

	/**
	 * Add badge and description to menu title.
	 *
	 * @param string $title Title.
	 * @param object $item Item.
	 * @param object $args Args.
	 * @param int    $depth Depth.
	 * @return string
	 */
	function rx_theme_menu_title_with_badge_description( $title, $item, $args, $depth ) {

		$badge       = get_post_meta( $item->ID, '_rx_menu_badge', true );
		$description = get_post_meta( $item->ID, '_rx_menu_description', true );

		$output = '<span class="rx-menu-title-text">' . esc_html( $title ) . '</span>';

		if ( $badge ) {
			$output .= ' <span class="rx-menu-badge">' . esc_html( $badge ) . '</span>';
		}

		if ( $description ) {
			$output .= '<span class="rx-menu-description">' . esc_html( $description ) . '</span>';
		}

		return $output;
	}
}
add_filter( 'nav_menu_item_title', 'rx_theme_menu_title_with_badge_description', 20, 4 );


/**
 * ------------------------------------------------------------
 * 17. Breadcrumb Helper
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_breadcrumbs' ) ) {

	/**
	 * Display breadcrumbs.
	 *
	 * @param array $args Args.
	 * @return void
	 */
	function rx_theme_breadcrumbs( $args = array() ) {

		if ( is_front_page() ) {
			return;
		}

		$defaults = array(
			'container_class' => 'rx-breadcrumbs',
			'home_text'       => esc_html__( 'Home', RX_THEME_TEXT_DOMAIN ),
			'separator'       => '<span class="rx-breadcrumb-separator" aria-hidden="true">›</span>',
		);

		$args = wp_parse_args( $args, $defaults );

		$items = array();

		$items[] = array(
			'title' => $args['home_text'],
			'url'   => home_url( '/' ),
		);

		if ( is_home() ) {
			$items[] = array(
				'title' => get_the_title( get_option( 'page_for_posts' ) ),
				'url'   => '',
			);
		} elseif ( is_singular() ) {

			$post_type = get_post_type();

			if ( 'post' === $post_type ) {
				$categories = get_the_category();

				if ( ! empty( $categories ) ) {
					$category = $categories[0];

					$items[] = array(
						'title' => $category->name,
						'url'   => get_category_link( $category->term_id ),
					);
				}
			} elseif ( 'page' !== $post_type ) {
				$post_type_object = get_post_type_object( $post_type );

				if ( $post_type_object && ! empty( $post_type_object->has_archive ) ) {
					$items[] = array(
						'title' => $post_type_object->labels->name,
						'url'   => get_post_type_archive_link( $post_type ),
					);
				}
			}

			if ( is_page() ) {
				$ancestors = array_reverse( get_post_ancestors( get_the_ID() ) );

				foreach ( $ancestors as $ancestor_id ) {
					$items[] = array(
						'title' => get_the_title( $ancestor_id ),
						'url'   => get_permalink( $ancestor_id ),
					);
				}
			}

			$items[] = array(
				'title' => get_the_title(),
				'url'   => '',
			);

		} elseif ( is_category() || is_tag() || is_tax() ) {

			$term = get_queried_object();

			if ( $term && ! is_wp_error( $term ) ) {
				$items[] = array(
					'title' => single_term_title( '', false ),
					'url'   => '',
				);
			}

		} elseif ( is_search() ) {

			$items[] = array(
				'title' => sprintf(
					/* translators: %s search query */
					esc_html__( 'Search results for: %s', RX_THEME_TEXT_DOMAIN ),
					get_search_query()
				),
				'url'   => '',
			);

		} elseif ( is_404() ) {

			$items[] = array(
				'title' => esc_html__( '404 Not Found', RX_THEME_TEXT_DOMAIN ),
				'url'   => '',
			);

		} elseif ( is_archive() ) {

			$items[] = array(
				'title' => get_the_archive_title(),
				'url'   => '',
			);
		}

		$items = apply_filters( 'rx_theme_breadcrumb_items', $items );

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

		echo '<nav class="' . esc_attr( $args['container_class'] ) . '" aria-label="' . esc_attr__( 'Breadcrumb', RX_THEME_TEXT_DOMAIN ) . '">';
		echo '<ol class="rx-breadcrumb-list" itemscope itemtype="https://schema.org/BreadcrumbList">';

		$count = count( $items );

		foreach ( $items as $index => $item ) {
			$position = $index + 1;
			$is_last  = $position === $count;

			echo '<li class="rx-breadcrumb-item" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">';

			if ( ! empty( $item['url'] ) && ! $is_last ) {
				echo '<a href="' . esc_url( $item['url'] ) . '" itemprop="item">';
				echo '<span itemprop="name">' . esc_html( wp_strip_all_tags( $item['title'] ) ) . '</span>';
				echo '</a>';
			} else {
				echo '<span itemprop="name" aria-current="page">' . esc_html( wp_strip_all_tags( $item['title'] ) ) . '</span>';
			}

			echo '<meta itemprop="position" content="' . esc_attr( $position ) . '">';

			if ( ! $is_last ) {
				echo wp_kses_post( $args['separator'] );
			}

			echo '</li>';
		}

		echo '</ol>';
		echo '</nav>';
	}
}


/**
 * ------------------------------------------------------------
 * 18. Category Menu Helper
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_category_menu' ) ) {

	/**
	 * Display category menu.
	 *
	 * @param array $args Args.
	 */
	function rx_theme_category_menu( $args = array() ) {

		$defaults = array(
			'taxonomy'     => 'category',
			'title_li'     => '',
			'show_count'   => false,
			'hide_empty'   => true,
			'orderby'      => 'name',
			'order'        => 'ASC',
			'depth'        => 2,
			'echo'         => false,
		);

		$args = wp_parse_args( $args, $defaults );

		$items = wp_list_categories( $args );

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

		echo '<nav class="rx-category-navigation" aria-label="' . esc_attr__( 'Category Menu', RX_THEME_TEXT_DOMAIN ) . '">';
		echo '<ul class="rx-category-menu">';
		echo wp_kses_post( $items );
		echo '</ul>';
		echo '</nav>';
	}
}


/**
 * ------------------------------------------------------------
 * 19. Page Menu Helper
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_page_menu' ) ) {

	/**
	 * Display page menu.
	 *
	 * @param array $args Args.
	 */
	function rx_theme_page_menu( $args = array() ) {

		$defaults = array(
			'title_li'    => '',
			'sort_column' => 'menu_order,post_title',
			'depth'       => 3,
			'echo'        => false,
		);

		$args = wp_parse_args( $args, $defaults );

		$items = wp_list_pages( $args );

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

		echo '<nav class="rx-page-navigation" aria-label="' . esc_attr__( 'Page Menu', RX_THEME_TEXT_DOMAIN ) . '">';
		echo '<ul class="rx-page-menu">';
		echo wp_kses_post( $items );
		echo '</ul>';
		echo '</nav>';
	}
}


/**
 * ------------------------------------------------------------
 * 20. Search Form Menu Item
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_add_search_to_primary_menu' ) ) {

	/**
	 * Add search button/item to selected menu.
	 *
	 * @param string $items Menu items.
	 * @param object $args Args.
	 * @return string
	 */
	function rx_theme_add_search_to_primary_menu( $items, $args ) {

		$allowed_locations = apply_filters(
			'rx_theme_search_menu_locations',
			array( 'primary', 'mobile' )
		);

		if ( empty( $args->theme_location ) || ! in_array( $args->theme_location, $allowed_locations, true ) ) {
			return $items;
		}

		$search_item  = '<li class="rx-menu-item rx-menu-search-item">';
		$search_item .= '<button class="rx-menu-search-toggle" type="button" aria-label="' . esc_attr__( 'Open search', RX_THEME_TEXT_DOMAIN ) . '" aria-expanded="false">';
		$search_item .= '<span class="rx-search-icon" aria-hidden="true">🔍</span>';
		$search_item .= '<span class="screen-reader-text">' . esc_html__( 'Search', RX_THEME_TEXT_DOMAIN ) . '</span>';
		$search_item .= '</button>';
		$search_item .= '</li>';

		return $items . $search_item;
	}
}
add_filter( 'wp_nav_menu_items', 'rx_theme_add_search_to_primary_menu', 10, 2 );


/**
 * ------------------------------------------------------------
 * 21. Login / Logout Menu Item
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_add_account_item_to_menu' ) ) {

	/**
	 * Add login/logout account item.
	 *
	 * @param string $items Menu items.
	 * @param object $args Args.
	 * @return string
	 */
	function rx_theme_add_account_item_to_menu( $items, $args ) {

		$allowed_locations = apply_filters(
			'rx_theme_account_menu_locations',
			array( 'account' )
		);

		if ( empty( $args->theme_location ) || ! in_array( $args->theme_location, $allowed_locations, true ) ) {
			return $items;
		}

		if ( is_user_logged_in() ) {
			$url   = wp_logout_url( home_url( '/' ) );
			$text  = esc_html__( 'Logout', RX_THEME_TEXT_DOMAIN );
			$class = 'rx-menu-logout-item';
		} else {
			$url   = wp_login_url( get_permalink() );
			$text  = esc_html__( 'Login', RX_THEME_TEXT_DOMAIN );
			$class = 'rx-menu-login-item';
		}

		$item  = '<li class="rx-menu-item ' . esc_attr( $class ) . '">';
		$item .= '<a class="rx-menu-link" href="' . esc_url( $url ) . '">' . esc_html( $text ) . '</a>';
		$item .= '</li>';

		return $items . $item;
	}
}
add_filter( 'wp_nav_menu_items', 'rx_theme_add_account_item_to_menu', 20, 2 );


/**
 * ------------------------------------------------------------
 * 22. Admin Bar Shortcut
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_admin_bar_menu_shortcut' ) ) {

	/**
	 * Add menu shortcut to admin bar.
	 *
	 * @param WP_Admin_Bar $wp_admin_bar Admin bar.
	 */
	function rx_theme_admin_bar_menu_shortcut( $wp_admin_bar ) {

		if ( ! current_user_can( 'edit_theme_options' ) ) {
			return;
		}

		$wp_admin_bar->add_node(
			array(
				'id'    => 'rx-edit-menus',
				'title' => esc_html__( 'RX Menus', RX_THEME_TEXT_DOMAIN ),
				'href'  => admin_url( 'nav-menus.php' ),
				'meta'  => array(
					'title' => esc_html__( 'Manage RX Theme Menus', RX_THEME_TEXT_DOMAIN ),
				),
			)
		);
	}
}
add_action( 'admin_bar_menu', 'rx_theme_admin_bar_menu_shortcut', 90 );


/**
 * ------------------------------------------------------------
 * 23. Menu Cache Helpers
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_flush_menu_cache' ) ) {

	/**
	 * Flush menu-related cache/transients.
	 */
	function rx_theme_flush_menu_cache() {
		delete_transient( 'rx_theme_menu_locations_cache' );
	}
}
add_action( 'wp_update_nav_menu', 'rx_theme_flush_menu_cache' );
add_action( 'wp_delete_nav_menu', 'rx_theme_flush_menu_cache' );
add_action( 'wp_create_nav_menu', 'rx_theme_flush_menu_cache' );


if ( ! function_exists( 'rx_theme_cached_menu_locations' ) ) {

	/**
	 * Get cached menu locations.
	 *
	 * @return array
	 */
	function rx_theme_cached_menu_locations() {

		$cache = get_transient( 'rx_theme_menu_locations_cache' );

		if ( false !== $cache && is_array( $cache ) ) {
			return $cache;
		}

		$locations = get_nav_menu_locations();

		set_transient( 'rx_theme_menu_locations_cache', $locations, HOUR_IN_SECONDS );

		return $locations;
	}
}


/**
 * ------------------------------------------------------------
 * 24. JSON-LD Site Navigation Schema
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_output_menu_json_ld' ) ) {

	/**
	 * Output JSON-LD for primary menu.
	 */
	function rx_theme_output_menu_json_ld() {

		if ( is_admin() || ! has_nav_menu( 'primary' ) ) {
			return;
		}

		$items = rx_theme_get_menu_items_by_location( 'primary' );

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

		$schema_items = array();

		foreach ( $items as $item ) {
			if ( ! empty( $item->menu_item_parent ) ) {
				continue;
			}

			if ( empty( $item->url ) || empty( $item->title ) ) {
				continue;
			}

			$schema_items[] = array(
				'@type' => 'SiteNavigationElement',
				'name'  => wp_strip_all_tags( $item->title ),
				'url'   => esc_url_raw( $item->url ),
			);
		}

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

		$schema = array(
			'@context' => 'https://schema.org',
			'@graph'   => $schema_items,
		);

		echo '<script type="application/ld+json" class="rx-menu-schema">';
		echo wp_json_encode( $schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
		echo '</script>';
	}
}
add_action( 'wp_head', 'rx_theme_output_menu_json_ld', 30 );


/**
 * ------------------------------------------------------------
 * 25. Mobile Menu Toggle Markup
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_mobile_menu_toggle' ) ) {

	/**
	 * Display mobile menu toggle button.
	 *
	 * @param array $args Args.
	 */
	function rx_theme_mobile_menu_toggle( $args = array() ) {

		$defaults = array(
			'target'      => 'rx-mobile-navigation',
			'label'       => esc_html__( 'Open Menu', RX_THEME_TEXT_DOMAIN ),
			'class'       => 'rx-mobile-menu-toggle',
			'icon'        => '☰',
			'echo'        => true,
		);

		$args = wp_parse_args( $args, $defaults );

		$output  = '<button class="' . esc_attr( $args['class'] ) . '" type="button" aria-controls="' . esc_attr( $args['target'] ) . '" aria-expanded="false">';
		$output .= '<span class="rx-mobile-menu-toggle-icon" aria-hidden="true">' . esc_html( $args['icon'] ) . '</span>';
		$output .= '<span class="rx-mobile-menu-toggle-text">' . esc_html( $args['label'] ) . '</span>';
		$output .= '</button>';

		if ( ! empty( $args['echo'] ) ) {
			echo wp_kses_post( $output );
			return;
		}

		return $output;
	}
}


/**
 * ------------------------------------------------------------
 * 26. Offcanvas Menu Markup
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_offcanvas_menu' ) ) {

	/**
	 * Display offcanvas menu wrapper.
	 *
	 * @param array $args Args.
	 */
	function rx_theme_offcanvas_menu( $args = array() ) {

		$defaults = array(
			'id'       => 'rx-offcanvas-menu',
			'title'    => esc_html__( 'Menu', RX_THEME_TEXT_DOMAIN ),
			'location' => has_nav_menu( 'offcanvas' ) ? 'offcanvas' : 'mobile',
		);

		$args = wp_parse_args( $args, $defaults );

		echo '<aside id="' . esc_attr( $args['id'] ) . '" class="rx-offcanvas-menu" aria-hidden="true">';
		echo '<div class="rx-offcanvas-menu-inner">';
		echo '<div class="rx-offcanvas-menu-header">';
		echo '<h2 class="rx-offcanvas-menu-title">' . esc_html( $args['title'] ) . '</h2>';
		echo '<button class="rx-offcanvas-close" type="button" aria-label="' . esc_attr__( 'Close Menu', RX_THEME_TEXT_DOMAIN ) . '">×</button>';
		echo '</div>';

		rx_theme_render_menu(
			$args['location'],
			array(
				'menu_id'         => 'rx-offcanvas-navigation-menu',
				'menu_class'      => 'rx-menu rx-offcanvas-navigation-menu',
				'container_id'    => 'rx-offcanvas-navigation',
				'container_class' => 'rx-navigation rx-offcanvas-navigation',
			)
		);

		echo '</div>';
		echo '</aside>';
		echo '<div class="rx-offcanvas-overlay" hidden></div>';
	}
}


/**
 * ------------------------------------------------------------
 * 27. Skip Link
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_skip_link' ) ) {

	/**
	 * Display skip link.
	 */
	function rx_theme_skip_link() {
		echo '<a class="rx-skip-link screen-reader-text" href="#content">' . esc_html__( 'Skip to content', RX_THEME_TEXT_DOMAIN ) . '</a>';
	}
}


/**
 * ------------------------------------------------------------
 * 28. Helpful Header Menu Layout Function
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_header_navigation' ) ) {

	/**
	 * Complete header navigation block.
	 */
	function rx_theme_header_navigation() {
		?>
		<div class="rx-header-navigation-wrap">
			<?php rx_theme_mobile_menu_toggle(); ?>

			<?php
			rx_theme_render_menu(
				'primary',
				array(
					'menu_id'         => 'rx-primary-menu',
					'menu_class'      => 'rx-menu rx-primary-menu',
					'container_id'    => 'rx-primary-navigation',
					'container_class' => 'rx-navigation rx-primary-navigation',
				)
			);
			?>
		</div>
		<?php
	}
}


/**
 * ------------------------------------------------------------
 * 29. Menu Depth Limiter
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_limit_menu_depth' ) ) {

	/**
	 * Optionally limit depth by location.
	 *
	 * @param array $args Args.
	 * @return array
	 */
	function rx_theme_limit_menu_depth( $args ) {

		if ( empty( $args['theme_location'] ) ) {
			return $args;
		}

		$depth_map = apply_filters(
			'rx_theme_menu_depth_map',
			array(
				'topbar'        => 1,
				'social'        => 1,
				'footer'        => 2,
				'footer_one'    => 2,
				'footer_two'    => 2,
				'footer_three'  => 2,
				'footer_four'   => 2,
				'legal'         => 1,
				'primary'       => 4,
				'mobile'        => 4,
				'offcanvas'     => 4,
				'mega_menu'     => 5,
			)
		);

		$location = sanitize_key( $args['theme_location'] );

		if ( isset( $depth_map[ $location ] ) ) {
			$args['depth'] = absint( $depth_map[ $location ] );
		}

		return $args;
	}
}
add_filter( 'wp_nav_menu_args', 'rx_theme_limit_menu_depth', 20 );


/**
 * ------------------------------------------------------------
 * 30. Auto Assign First Menu To Primary Location
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_auto_assign_primary_menu' ) ) {

	/**
	 * Auto assign first menu to primary if empty.
	 */
	function rx_theme_auto_assign_primary_menu() {

		if ( has_nav_menu( 'primary' ) ) {
			return;
		}

		$menus = wp_get_nav_menus();

		if ( empty( $menus ) || is_wp_error( $menus ) ) {
			return;
		}

		$locations = get_theme_mod( 'nav_menu_locations', array() );

		if ( empty( $locations['primary'] ) ) {
			$locations['primary'] = absint( $menus[0]->term_id );
			set_theme_mod( 'nav_menu_locations', $locations );
		}
	}
}
add_action( 'after_switch_theme', 'rx_theme_auto_assign_primary_menu' );


/**
 * ------------------------------------------------------------
 * 31. Create Default Menu On Theme Activation
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_create_default_menu' ) ) {

	/**
	 * Create a starter menu if no menu exists.
	 */
	function rx_theme_create_default_menu() {

		$menus = wp_get_nav_menus();

		if ( ! empty( $menus ) ) {
			return;
		}

		$menu_name = 'RX Primary Menu';
		$menu_id   = wp_create_nav_menu( $menu_name );

		if ( is_wp_error( $menu_id ) ) {
			return;
		}

		wp_update_nav_menu_item(
			$menu_id,
			0,
			array(
				'menu-item-title'   => esc_html__( 'Home', RX_THEME_TEXT_DOMAIN ),
				'menu-item-url'     => home_url( '/' ),
				'menu-item-status'  => 'publish',
				'menu-item-type'    => 'custom',
			)
		);

		$page_ids = get_posts(
			array(
				'post_type'      => 'page',
				'post_status'    => 'publish',
				'posts_per_page' => 5,
				'fields'         => 'ids',
			)
		);

		foreach ( $page_ids as $page_id ) {
			wp_update_nav_menu_item(
				$menu_id,
				0,
				array(
					'menu-item-title'     => get_the_title( $page_id ),
					'menu-item-object-id' => $page_id,
					'menu-item-object'    => 'page',
					'menu-item-type'      => 'post_type',
					'menu-item-status'    => 'publish',
				)
			);
		}

		$locations            = get_theme_mod( 'nav_menu_locations', array() );
		$locations['primary'] = absint( $menu_id );

		set_theme_mod( 'nav_menu_locations', $locations );
	}
}
add_action( 'after_switch_theme', 'rx_theme_create_default_menu' );


/**
 * ------------------------------------------------------------
 * 32. Small Inline Script For Menu Accessibility
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_menu_inline_script' ) ) {

	/**
	 * Add small vanilla JS for submenu toggles.
	 */
	function rx_theme_menu_inline_script() {
		?>
		<script>
			(function () {
				'use strict';

				function closeSiblingSubmenus(button) {
					var parentLi = button.closest('.rx-menu-item');
					if (!parentLi || !parentLi.parentNode) {
						return;
					}

					var siblingButtons = parentLi.parentNode.querySelectorAll(':scope > .rx-menu-item > .rx-submenu-toggle');

					siblingButtons.forEach(function (siblingButton) {
						if (siblingButton !== button) {
							siblingButton.setAttribute('aria-expanded', 'false');

							var siblingLink = siblingButton.parentNode.querySelector('.rx-menu-link[aria-haspopup="true"]');
							if (siblingLink) {
								siblingLink.setAttribute('aria-expanded', 'false');
							}

							siblingButton.parentNode.classList.remove('rx-submenu-open');
						}
					});
				}

				document.addEventListener('click', function (event) {
					var button = event.target.closest('.rx-submenu-toggle');

					if (!button) {
						return;
					}

					var expanded = button.getAttribute('aria-expanded') === 'true';
					var parent = button.closest('.rx-menu-item');
					var link = parent ? parent.querySelector('.rx-menu-link[aria-haspopup="true"]') : null;

					closeSiblingSubmenus(button);

					button.setAttribute('aria-expanded', expanded ? 'false' : 'true');

					if (link) {
						link.setAttribute('aria-expanded', expanded ? 'false' : 'true');
					}

					if (parent) {
						parent.classList.toggle('rx-submenu-open', !expanded);
					}
				});

				document.addEventListener('keydown', function (event) {
					if (event.key !== 'Escape') {
						return;
					}

					document.querySelectorAll('.rx-submenu-toggle[aria-expanded="true"]').forEach(function (button) {
						button.setAttribute('aria-expanded', 'false');

						var parent = button.closest('.rx-menu-item');
						var link = parent ? parent.querySelector('.rx-menu-link[aria-haspopup="true"]') : null;

						if (link) {
							link.setAttribute('aria-expanded', 'false');
						}

						if (parent) {
							parent.classList.remove('rx-submenu-open');
						}
					});
				});

				document.addEventListener('click', function (event) {
					var mobileToggle = event.target.closest('.rx-mobile-menu-toggle');

					if (!mobileToggle) {
						return;
					}

					var targetId = mobileToggle.getAttribute('aria-controls');
					var target = targetId ? document.getElementById(targetId) : null;
					var expanded = mobileToggle.getAttribute('aria-expanded') === 'true';

					mobileToggle.setAttribute('aria-expanded', expanded ? 'false' : 'true');

					if (target) {
						target.classList.toggle('rx-mobile-navigation-open', !expanded);
					}

					document.body.classList.toggle('rx-mobile-menu-open', !expanded);
				});
			})();
		</script>
		<?php
	}
}
add_action( 'wp_footer', 'rx_theme_menu_inline_script', 50 );


/**
 * ------------------------------------------------------------
 * 33. Optional Basic Menu CSS
 * ------------------------------------------------------------
 */

if ( ! function_exists( 'rx_theme_menu_inline_css' ) ) {

	/**
	 * Output basic menu CSS.
	 */
	function rx_theme_menu_inline_css() {
		?>
		<style>
			.rx-menu,
			.rx-menu ul {
				list-style: none;
				margin: 0;
				padding: 0;
			}

			.rx-menu {
				display: flex;
				flex-wrap: wrap;
				align-items: center;
				gap: 0.25rem;
			}

			.rx-menu-item {
				position: relative;
			}

			.rx-menu-link {
				display: inline-flex;
				align-items: center;
				gap: 0.35rem;
				text-decoration: none;
			}

			.rx-sub-menu {
				display: none;
				position: absolute;
				top: 100%;
				left: 0;
				min-width: 220px;
				z-index: 999;
				background: #fff;
				box-shadow: 0 10px 30px rgba(0,0,0,0.08);
			}

			.rx-menu-item:hover > .rx-sub-menu,
			.rx-menu-item:focus-within > .rx-sub-menu,
			.rx-menu-item.rx-submenu-open > .rx-sub-menu {
				display: block;
			}

			.rx-submenu-toggle {
				display: inline-flex;
				align-items: center;
				justify-content: center;
				cursor: pointer;
			}

			.rx-mobile-menu-toggle {
				display: none;
			}

			.rx-menu-badge {
				display: inline-block;
				font-size: 0.7em;
				line-height: 1;
				padding: 0.2em 0.45em;
				border-radius: 999px;
				background: currentColor;
				color: #fff;
			}

			.rx-menu-description {
				display: block;
				font-size: 0.8em;
				opacity: 0.75;
			}

			.rx-skip-link:focus {
				position: fixed;
				top: 10px;
				left: 10px;
				z-index: 99999;
				display: block;
				padding: 10px 14px;
				background: #fff;
				color: #000;
			}

			@media (max-width: 768px) {
				.rx-mobile-menu-toggle {
					display: inline-flex;
				}

				.rx-primary-navigation,
				.rx-mobile-navigation {
					display: none;
				}

				.rx-primary-navigation.rx-mobile-navigation-open,
				.rx-mobile-navigation.rx-mobile-navigation-open,
				.rx-mobile-menu-open .rx-primary-navigation,
				.rx-mobile-menu-open .rx-mobile-navigation {
					display: block;
				}

				.rx-menu {
					display: block;
				}

				.rx-sub-menu {
					position: static;
					box-shadow: none;
					min-width: 0;
				}
			}
		</style>
		<?php
	}
}
add_action( 'wp_head', 'rx_theme_menu_inline_css', 80 );


/**
 * ------------------------------------------------------------
 * 34. Template Usage Notes
 * ------------------------------------------------------------
 *
 * In header.php:
 *
 * <?php rx_theme_skip_link(); ?>
 * <?php rx_theme_header_navigation(); ?>
 *
 * Or:
 *
 * <?php rx_theme_primary_menu(); ?>
 *
 * In mobile area:
 *
 * <?php rx_theme_mobile_menu_toggle(); ?>
 * <?php rx_theme_mobile_menu(); ?>
 *
 * In footer.php:
 *
 * <?php rx_theme_footer_menu(); ?>
 * <?php rx_theme_social_menu(); ?>
 *
 * For breadcrumbs:
 *
 * <?php rx_theme_breadcrumbs(); ?>
 */

Use in header.php:

<?php rx_theme_skip_link(); ?>

<header id="masthead" class="rx-site-header">
	<?php rx_theme_header_navigation(); ?>
</header>

Use in footer.php:

<footer id="colophon" class="rx-site-footer">
	<?php rx_theme_footer_menu(); ?>
	<?php rx_theme_social_menu(); ?>
</footer>

This file gives your RX theme a strong menu system with primary, mobile, footer, social, mega-menu-ready, accessibility, schema, fallback, admin custom fields, and helper functions.

Leave a Reply

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