Skip to content

WordPress Menu

Register menu locations

register_nav_menus()

Register each menu with a location (like a unique slug) and a description. The description will be shown on Wp-Admin > Appearance > Menus > Manage Locations.

register_nav_menus(
  array(
    // 'location'  => 'description',
    'main_menu' => 'Main menu',
    'sub_menu1' => 'Sub menu 1 (top left)',
    'sub_menu2' => 'Sub menu 2 (top right)',
    'sub_menu3' => 'Sub menu 3 (bottom left)',
    'sub_menu4' => 'Sub menu 4 (bottom right)',
  )
);

Display the menu in a theme

WordPress documentation: wp_nav_menu()

menu can be the menu ID, slug, name, or object.

Display menu using the theme_location from the above setup. That way the theme will not rely on a specific name of a menu existing, but it does rely on a menu being set for that location.

menu_class is the css class that will be added to the ul of the menu output. It can be more than one class, e.g. 'sub-menu position-2'

Setting container to false will remove the container element (default is a div) that normally contains the ul.

<?php
  wp_nav_menu( 
    array(
      'theme_location'  => 'main_menu',
      'menu_class'      => 'main-menu',
      'items_wrap'      => '<ul class="%2$s">%3$s</ul>', // remove the id on the ul element
      'container'       => false
    )
  ); 
?>

Display a menu using a name (relies on this name being added through the WordPress admin).

<?php
  wp_nav_menu( 
    array(
      'menu' => 'Menu 1', 
    )
  ); 
?>

Assuming we use the menu class .main-menu as per above, we can style the menu like so. This assumes a custom breakpoint has been added in Tailwind called menu. See Tailwind Breakpoints

/*--------------------------------------------------------------
# Main Menu 
--------------------------------------------------------------*/

.main-menu {
  @apply hidden menu:flex space-x-12;
}

.main-menu > li > a {
  @apply text-base md:text-xl transition-opacity duration-300 hover:opacity-70;

  .dark-header & {
    @apply text-white;
  }
}

If the menu has a second level sub-menu use the following css. This assumes a custom breakpoint has been added in Tailwind called menu. See Tailwind Breakpoints

/*--------------------------------------------------------------
# Sub-menu 
--------------------------------------------------------------*/

/* Desktop menu */
@media screen(menu) {
  .sub-menu {
    @apply absolute hidden opacity-0 w-max;
  }

  .main-menu li:hover > .sub-menu,
  .main-menu li:focus-within > .sub-menu {
    @apply visible block opacity-100;
  }

  .sub-menu > li > a {
    @apply block px-2 font-semibold leading-10 tracking-wider uppercase transition-opacity duration-300 md:text-sm md:leading-10;
  }
}

/* Mobile menu overlay */
.mobile-menu {
  
  .sub-menu {
    @apply pl-5;
  }

  a {
    @apply block py-1 leading-10 tracking-wider;
  }

  .current_page_item {
    @apply underline underline-offset-8;
  }

}

Credit: Based on https://css-tricks.com/solved-with-css-dropdown-menus/

Overlay modal menu

https://micromodal.now.sh/

Micromodal.js is a lightweight, configurable and a11y-enabled modal library written in pure JavaScript.

Save micromodal.min.js to /js folder.

Enqueue in functions.php.

wp_enqueue_script( 'projectX-micromodal', get_stylesheet_directory_uri() . '/js/micromodal.min.js' );

Add html to footer.php. The micromodal code also changes the aria-expanded attribute to the current state. Note the svg in the close button has the class pointer-events-none to endure the entire button is clickable.

<!-- Modal Mobile Menu -->
<div id="menu-modal" aria-hidden="true">
  <div 
    aria-label="menu"
    aria-modal="true" 
    class="h-screen p-5 overflow-auto bg-white modal-overlay" 
    role="dialog" 
    tabindex="-1" 
  >

      <header class="flex justify-between">
        <a 
          href="<?php echo esc_url( home_url( '/' ) ); ?>" 
          rel="home" 
          title="Return to the homepage"
        >
          Logo
        </a>
        
        <button aria-label="Close modal" data-micromodal-close class="p-4">
          <svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M249-193.434 193.434-249l231-231-231-231L249-766.566l231 231 231-231L766.566-711l-231 231 231 231L711-193.434l-231-231-231 231Z" class="fill-white pointer-events-none"/></svg>
        </button>
      </header>

      <div id="menu-modal-content">
        Menu items
      </div>

  </div>
</div>

<script>
MicroModal.init({
  disableScroll: true, 
  awaitCloseAnimation: true, 
  onShow: modal => {
    document.getElementById("hamburger").setAttribute('aria-expanded', true);
  }, 
  onClose: modal => {
    document.getElementById("hamburger").setAttribute('aria-expanded', false);
  }
});
</script>

Add the attribute to the hamburger button. menu-modal below must match the id of the main <div> above.

<button data-micromodal-trigger="menu-modal"></button>

Full hamburger html example. This assumes the custom Tailwind breakpoint menu has been added to the Tailwind config. See Tailwind Breakpoints

<nav class="main-navigation" aria-labelledby="hamburger">
  
  <button
    id="hamburger"
    class="leading-none rounded hamburger focus:outline-none focus-visible:ring-2 focus-visible:ring-black focus-visible:ring-offset-4 menu:hidden"
    aria-controls="menu-modal"
    aria-expanded="false"
    aria-label="navigation menu"
    data-micromodal-trigger="menu-modal"
  >
    <svg xmlns="http://www.w3.org/2000/svg" width="41" height="30" class="menu-desktop"><g fill="#ffffff"><path d="M7 0h34v6H7z"/><path d="M0 12h41v6H0z"/><path d="M12 24h29v6H12z"/></g></svg>
  </button>

</nav>

Add the css for the modal.

/*--------------------------------------------------------------
# Menu Modal
--------------------------------------------------------------*/

#menu-modal.is-open {
  z-index: 99999; /* This is the minimum to be above admin bar */  
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

@keyframes mmfadeIn {
  from { opacity: 0; transform: scale(0.95); }
    to { opacity: 1; transform: scale(1); }
}

@keyframes mmfadeOut {
  from { opacity: 1; transform: scale(1); }
    to { opacity: 0; transform: scale(0.95); }
}

#menu-modal {
  display: none;
}

#menu-modal.is-open {
  display: block;
}

#menu-modal[aria-hidden="false"] .modal-overlay {
  animation: mmfadeIn .3s cubic-bezier(0.0, 0.0, 0.2, 1);
}

#menu-modal[aria-hidden="true"] .modal-overlay {
  animation: mmfadeOut .3s cubic-bezier(0.0, 0.0, 0.2, 1);
}