Skip to main content

Course Progress

Loading...

🔗 Implementing Menus in Themes

Display navigation menus in your WordPress theme

Master wp_nav_menu() and create responsive navigation systems

Learning Objectives

  • Understand wp_nav_menu() function and parameters
  • Display menus in theme templates
  • Configure menu containers and classes
  • Style navigation menus with CSS
  • Create responsive mobile menus
  • Handle menu item classes and IDs
  • Implement dropdown/submenu navigation
  • Add menu item descriptions and icons

The wp_nav_menu() Function

The wp_nav_menu() function is the primary way to display navigation menus in WordPress themes. It provides extensive customization options through its parameters.

💡
Key Concept
wp_nav_menu() generates the complete HTML for a navigation menu, including the container, menu items, and proper CSS classes for styling and JavaScript interaction.

Basic Menu Implementation

Simple Menu Display

<?php
// In header.php - Basic menu display
wp_nav_menu( array(
    'theme_location' => 'primary'
) );

// With more parameters
wp_nav_menu( array(
    'theme_location'  => 'primary',
    'menu_id'         => 'primary-menu',
    'menu_class'      => 'nav-menu',
    'container'       => 'nav',
    'container_class' => 'main-navigation',
    'container_id'    => 'site-navigation',
) );

Complete Menu Implementation in header.php

<header id="masthead" class="site-header">
    <div class="container">
        <div class="site-branding">
            <h1 class="site-title">
                <a href="<?php echo esc_url( home_url( '/' ) ); ?>">
                    <?php bloginfo( 'name' ); ?>
                </a>
            </h1>
        </div>
        
        <?php if ( has_nav_menu( 'primary' ) ) : ?>
            <nav id="site-navigation" class="main-navigation" role="navigation">
                <button class="menu-toggle" aria-controls="primary-menu" aria-expanded="false">
                    <?php esc_html_e( 'Menu', 'mytheme' ); ?>
                </button>
                
                <?php
                wp_nav_menu( array(
                    'theme_location'  => 'primary',
                    'menu_id'         => 'primary-menu',
                    'menu_class'      => 'menu nav-menu',
                    'container_class' => 'menu-primary-container',
                    'fallback_cb'     => 'mytheme_primary_menu_fallback',
                    'depth'           => 3,
                    'link_before'     => '<span>',
                    'link_after'      => '</span>',
                ) );
                ?>
            </nav>
        <?php endif; ?>
    </div>
</header>

wp_nav_menu() Parameters

Parameter Description Default Value
theme_location Registered menu location to use ''
menu Menu ID, slug, or name ''
container Container element (div, nav, etc.) 'div'
container_class CSS class for container 'menu-{menu slug}-container'
container_id ID for container ''
menu_class CSS class for the menu 'menu'
menu_id ID for the menu '{menu slug}'
echo Whether to echo or return true
fallback_cb Fallback function if menu doesn't exist 'wp_page_menu'
before Text before link markup ''
after Text after link markup ''
link_before Text before link text ''
link_after Text after link text ''
items_wrap How items should be wrapped '<ul id="%1$s" class="%2$s">%3$s</ul>'
depth How many levels of hierarchy 0 (all)
walker Custom walker class ''

Common Menu Implementations

Header Navigation Menu

<?php
// Primary navigation with all options
function mytheme_primary_navigation() {
    if ( has_nav_menu( 'primary' ) ) {
        ?>
        <nav id="site-navigation" class="main-navigation" role="navigation" aria-label="<?php esc_attr_e( 'Primary Menu', 'mytheme' ); ?>">
            <button class="menu-toggle" aria-controls="primary-menu" aria-expanded="false">
                <span class="screen-reader-text"><?php _e( 'Menu', 'mytheme' ); ?></span>
                <span class="menu-toggle-icon">
                    <span></span>
                    <span></span>
                    <span></span>
                </span>
            </button>
            
            <?php
            wp_nav_menu( array(
                'theme_location'  => 'primary',
                'menu_id'         => 'primary-menu',
                'menu_class'      => 'nav-menu',
                'container'       => 'div',
                'container_class' => 'primary-menu-container',
                'items_wrap'      => '<ul id="%1$s" class="%2$s">%3$s</ul>',
                'fallback_cb'     => false,
            ) );
            ?>
        </nav>
        <?php
    }
}

Footer Menu Implementation

<?php
// Footer navigation - single level
function mytheme_footer_navigation() {
    if ( has_nav_menu( 'footer' ) ) {
        wp_nav_menu( array(
            'theme_location'  => 'footer',
            'menu_id'         => 'footer-menu',
            'menu_class'      => 'footer-menu',
            'container'       => 'nav',
            'container_class' => 'footer-navigation',
            'container_aria_label' => __( 'Footer Menu', 'mytheme' ),
            'depth'           => 1, // Only one level
            'fallback_cb'     => false,
            'items_wrap'      => '<ul id="%1$s" class="%2$s">%3$s</ul>',
        ) );
    }
}

// In footer.php
?>
<footer class="site-footer">
    <div class="footer-content">
        <?php mytheme_footer_navigation(); ?>
        
        <div class="copyright">
            <p>© <?php echo date('Y'); ?> <?php bloginfo( 'name' ); ?></p>
        </div>
    </div>
</footer>

Social Links Menu

<?php
// Social links menu with icons
function mytheme_social_menu() {
    if ( has_nav_menu( 'social' ) ) {
        wp_nav_menu( array(
            'theme_location'  => 'social',
            'menu_id'         => 'social-menu',
            'menu_class'      => 'social-links',
            'container'       => 'nav',
            'container_class' => 'social-navigation',
            'container_aria_label' => __( 'Social Links', 'mytheme' ),
            'link_before'     => '<span class="screen-reader-text">',
            'link_after'      => '</span>',
            'depth'           => 1,
            'fallback_cb'     => false,
            'items_wrap'      => '<ul id="%1$s" class="%2$s" aria-label="' . esc_attr__( 'Social links', 'mytheme' ) . '">%3$s</ul>',
        ) );
    }
}

Responsive Menu Implementation

Mobile Menu with JavaScript

// menu-toggle.js
document.addEventListener('DOMContentLoaded', function() {
    const menuToggle = document.querySelector('.menu-toggle');
    const navigation = document.querySelector('.main-navigation');
    const menu = document.querySelector('.nav-menu');
    
    if (!menuToggle || !navigation) return;
    
    menuToggle.addEventListener('click', function() {
        const isExpanded = menuToggle.getAttribute('aria-expanded') === 'true';
        
        menuToggle.setAttribute('aria-expanded', !isExpanded);
        navigation.classList.toggle('toggled');
        
        // Trap focus for accessibility
        if (!isExpanded) {
            menu.querySelector('a').focus();
        }
    });
    
    // Close menu with Escape key
    document.addEventListener('keydown', function(e) {
        if (e.key === 'Escape' && navigation.classList.contains('toggled')) {
            menuToggle.click();
            menuToggle.focus();
        }
    });
    
    // Handle submenu toggles
    const subMenuToggles = document.querySelectorAll('.menu-item-has-children > a');
    
    subMenuToggles.forEach(function(toggle) {
        toggle.addEventListener('click', function(e) {
            if (window.innerWidth <= 768) {
                e.preventDefault();
                const subMenu = toggle.nextElementSibling;
                subMenu.classList.toggle('toggled-on');
                toggle.setAttribute('aria-expanded', 
                    subMenu.classList.contains('toggled-on'));
            }
        });
    });
});

Responsive Menu CSS

/* Desktop Navigation */
.main-navigation {
    display: block;
}

.nav-menu {
    display: flex;
    list-style: none;
    margin: 0;
    padding: 0;
}

.nav-menu li {
    position: relative;
}

.nav-menu a {
    display: block;
    padding: 1rem 1.5rem;
    text-decoration: none;
    color: #333;
    transition: all 0.3s ease;
}

.nav-menu a:hover,
.nav-menu .current-menu-item > a {
    background: #667eea;
    color: white;
}

/* Dropdown Menus */
.nav-menu .sub-menu {
    position: absolute;
    top: 100%;
    left: 0;
    min-width: 200px;
    background: white;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
    opacity: 0;
    visibility: hidden;
    transition: all 0.3s ease;
    z-index: 999;
}

.nav-menu li:hover > .sub-menu {
    opacity: 1;
    visibility: visible;
}

/* Mobile Menu Toggle */
.menu-toggle {
    display: none;
    background: transparent;
    border: none;
    padding: 0.5rem;
    cursor: pointer;
}

.menu-toggle-icon span {
    display: block;
    width: 25px;
    height: 3px;
    background: #333;
    margin: 5px 0;
    transition: all 0.3s ease;
}

/* Mobile Styles */
@media (max-width: 768px) {
    .menu-toggle {
        display: block;
    }
    
    .nav-menu {
        display: none;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        background: white;
        flex-direction: column;
        box-shadow: 0 2px 5px rgba(0,0,0,0.1);
    }
    
    .main-navigation.toggled .nav-menu {
        display: flex;
    }
    
    .nav-menu .sub-menu {
        position: static;
        opacity: 1;
        visibility: visible;
        box-shadow: none;
        padding-left: 1rem;
        display: none;
    }
    
    .nav-menu .sub-menu.toggled-on {
        display: block;
    }
    
    /* Animate hamburger icon */
    .main-navigation.toggled .menu-toggle-icon span:nth-child(1) {
        transform: rotate(45deg) translate(5px, 5px);
    }
    
    .main-navigation.toggled .menu-toggle-icon span:nth-child(2) {
        opacity: 0;
    }
    
    .main-navigation.toggled .menu-toggle-icon span:nth-child(3) {
        transform: rotate(-45deg) translate(7px, -6px);
    }
}

Menu Styling Examples

Advanced Menu Techniques

Adding Custom Classes and Attributes

<?php
// Add custom classes to menu items
function mytheme_nav_menu_css_class( $classes, $item, $args ) {
    // Add class to specific menu location
    if ( $args->theme_location == 'primary' ) {
        $classes[] = 'primary-menu-item';
    }
    
    // Add class based on post type
    if ( $item->object == 'page' ) {
        $classes[] = 'menu-page-item';
    }
    
    // Add class for external links
    if ( strpos( $item->url, home_url() ) === false ) {
        $classes[] = 'external-link';
    }
    
    return $classes;
}
add_filter( 'nav_menu_css_class', 'mytheme_nav_menu_css_class', 10, 3 );

// Add attributes to menu links
function mytheme_nav_menu_link_attributes( $atts, $item, $args ) {
    // Add target="_blank" to external links
    if ( strpos( $item->url, home_url() ) === false ) {
        $atts['target'] = '_blank';
        $atts['rel'] = 'noopener noreferrer';
    }
    
    // Add data attributes
    $atts['data-menu-item-id'] = $item->ID;
    
    return $atts;
}
add_filter( 'nav_menu_link_attributes', 'mytheme_nav_menu_link_attributes', 10, 3 );

// Add menu item description
function mytheme_nav_menu_item_description( $item_output, $item, $depth, $args ) {
    if ( $item->description ) {
        $item_output = str_replace( 
            $args->link_after . '',
            '<span class="menu-item-description">' . $item->description . '</span>' . $args->link_after . '',
            $item_output
        );
    }
    return $item_output;
}
add_filter( 'walker_nav_menu_start_el', 'mytheme_nav_menu_item_description', 10, 4 );

Best Practices

Menu Implementation Best Practices

  • Always check for menu existence: Use has_nav_menu() before displaying
  • Provide semantic HTML: Use nav element and proper ARIA labels
  • Make menus keyboard accessible: Ensure tab navigation works
  • Add mobile toggle: Implement responsive menu for small screens
  • Use proper fallbacks: Handle cases when no menu is assigned
  • Cache complex menus: Use transients for performance
  • Test with screen readers: Ensure accessibility compliance
  • Minimize depth: Limit to 2-3 levels for usability
Avoid hardcoding menu HTML. Always use wp_nav_menu() to ensure users can manage menus through the WordPress admin.

Practice Exercise

💻
Implement Complete Navigation System

Create a fully functional navigation system:

  1. Display primary menu in header
  2. Add mobile menu toggle button
  3. Implement responsive menu CSS
  4. Create footer menu with single level
  5. Add social links menu with icons
  6. Style dropdown/submenu items
  7. Add keyboard navigation support
  8. Implement menu toggle JavaScript
  9. Add custom classes to menu items
  10. Test accessibility with screen reader

Additional Resources