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.