Responsive WordPress menu

Today I’ll show you how to create a responsive WordPress menu for your theme. I’ve been using the code below on all our themes ,  with minimal changes to the CSS only.

This topic is a bit technical, you’ll need a beginners understanding of HTML,CSS & a bit ph php, you can just copy paste the code below, but where’s the fun in that 😀

  1. WordPress menu walker

The menu walker handles all the logic part of the menu

 

<?php

class Aperture_Menu_Walker extends Walker {

var $db_fields = array( 'parent' =>'menu_item_parent', 'id' => 'db_id' );

function start_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat("\t", $depth);
$output .= "\n$indent<ul>\n";
}

function end_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat("\t", $depth);
$output .= "$indent</ul>\n";
}

function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {

global $wp_query;
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;

/* Add active class */
if(in_array('current-menu-item', $classes)) {
$classes[] = 'active';
unset($classes['current-menu-item']);
}

/* Check for children */
$children = get_posts(array('post_type' => 'nav_menu_item', 'nopaging' => true, 'numberposts' => 1, 'meta_key' =>'_menu_item_menu_item_parent', 'meta_value' => $item->ID));
if (!empty($children)) {
$classes[] = 'has-sub';
}

$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';

$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
$id = $id ? ' id="' . esc_attr( $id ) . '"' : '';

$output .= $indent . '<li' . $id . $value . $class_names .'>';

$attributes = ! empty( $item->attr_title ) ? ' title="' . esc_attr( $item->attr_title ) .'"' : '';
$attributes .= ! empty( $item->target ) ? ' target="' . esc_attr( $item->target ) .'"' : '';
$attributes .= ! empty( $item->xfn ) ? ' rel="' . esc_attr( $item->xfn ) .'"' : '';
$attributes .= ! empty( $item->url ) ? ' href="' . esc_attr( $item->url ) .'"' : '';

$item_output = $args->before;
$item_output .= '<a'. $attributes .'><span>';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= '</span></a>';
$item_output .= $args->after;

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

function end_el( &$output, $item, $depth = 0, $args = array() ) {
$output .= "</li>\n";
}
/**
* Menu Fallback
* =============
* If this function is assigned to the wp_nav_menu's fallback_cb variable
* and a manu has not been assigned to the theme location in the WordPress
* menu manager the function with display nothing to a non-logged in user,
* and will add a link to the WordPress menu manager if logged in as an admin.
*
* @param array $args passed from the wp_nav_menu function.
*
*/
public static function fallback( $args ) {
if ( current_user_can( 'manage_options' ) ) {

extract( $args );

$fb_output = null;

if ( $container ) {
$fb_output = '<' . $container;

if ( $container_id )
$fb_output .= ' id="' . $container_id . '"';

if ( $container_class )
$fb_output .= ' class="' . $container_class . '"';

$fb_output .= '>';
}

$fb_output .= '<ul';

if ( $menu_id )
$fb_output .= ' id="' . $menu_id . '"';

if ( $menu_class )
$fb_output .= ' class="' . $menu_class . '"';

$fb_output .= '>';
$fb_output .= '<li><a href="' . admin_url( 'nav-menus.php' ) . '">.'. esc_html__('Add a menu','neptune-real-estate').'</a></li>';
$fb_output .= '</ul>';

if ( $container )
$fb_output .= '</' . $container . '>';

echo esc_html($fb_output);
}
}

}

2. Menu Location

We need to display the menu we just created above, somewhere on the header.php preferably .

<nav id="site-navigation" class="main-navigation">
<?php 
wp_nav_menu(array(
'container_id' => 'aperturemenu',
'theme_location' => 'menu-1',
'fallback_cb' => 'Aperture_Menu_Walker::fallback',
'walker' => new Aperture_Menu_Walker()
));?>

</nav>

wp_nav_menu : this display the WordPress menu, takes a few parameters like ‘container_id’, this is the actual div ID surrounding the menu.

Theme_location‘: this is the theme menu location as registered in your functions.php

Fallback_cb: this is the fallback menu, it will be displayed if the user is logged in and no menu selected, it adds an ‘Add Menu’  link.

3. Javascript

A responsive WordPress menu should not be hidden on different devices, it should just be restyled, we do this by adding the responsive menu button and an ‘open’ class for easier styling. Make sure you’ve enqueued the js file in your functions.php

jQuery(document).ready(function($){
$('#aperturemenu').prepend('<div id="menu-button">Menu</div>');
$('#aperturemenu #menu-button').on('click', function(){
var menu = $(this).next('ul');
if (menu.hasClass('open')) {
menu.removeClass('open');
} else {
menu.addClass('open');
}
});
});

3. CSS Styling

You can’t build a responsive WordPress menu without some CSS, this styles everything from hover effects, link colors & media queries. This should go somewhere in style.css

/*--------------------------------------------------------------
## Menus
--------------------------------------------------------------*/
#aperturemenu {
background: transparent;
margin: 0;
width: auto;
padding: 0;
line-height: 1;
display: block;
position: relative;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}

#aperturemenu ul {
list-style: none;
margin: 0 auto;
padding: 0;
float: none;
text-align: right;
padding-top: 15px;
}

#aperturemenu ul:after,
#aperturemenu:after {
content: ' ';
display: block;
font-size: 0;
height: 0;
clear: both;
visibility: hidden;
}

#aperturemenu a {
font-family: Montserrat, "Helvetica Neue", Helvetica, Arial, sans-serif;
}

#aperturemenu ul li {
margin: 0;
padding: 0;
display: block;
position: relative;

}

#aperturemenu ul li a {
text-decoration: none;
display: block;
margin: 0;
-webkit-transition: color .2s ease;
-moz-transition: color .2s ease;
-ms-transition: color .2s ease;
-o-transition: color .2s ease;
transition: color .2s ease;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;

}

#aperturemenu ul li a {
display: inline-block;
}
#aperturemenu ul li a:after {
content: '';
display: block;
height: 2px;
width: 0;
margin: 10px 0 0;
background: transparent;
transition: width .5s ease, background-color .5s ease;
}
#aperturemenu ul li a:hover:after {
width: 100%;
background: #eee;
}


#aperturemenu ul li ul {
position: absolute;
left: -9999px;
top: auto;
z-index: 9999;
}

#aperturemenu ul li ul li {
max-height: 0;
position: absolute;
-webkit-transition: max-height 0.4s ease-out;
-moz-transition: max-height 0.4s ease-out;
-ms-transition: max-height 0.4s ease-out;
-o-transition: max-height 0.4s ease-out;
transition: max-height 0.4s ease-out;
background: #ffffff;
}

#aperturemenu ul li ul li.has-sub:after {
display: block;
position: absolute;
content: '';
height: 10px;
width: 10px;
border-radius: 5px;
background: #000000;
z-index: 1;
top: 13px;
right: 15px;
}

#aperturemenu.align-right ul li ul li.has-sub:after {
right: auto;
left: 15px;
}

#aperturemenu ul li ul li.has-sub:before {
display: block;
position: absolute;
content: '';
height: 0;
width: 0;
border: 3px solid transparent;
border-left-color: #ffffff;
z-index: 2;
top: 15px;
right: 15px;
}

#aperturemenu.align-right ul li ul li.has-sub:before {
right: auto;
left: 15px;
border-left-color: transparent;
border-right-color: #ffffff;
}

#aperturemenu ul li ul li a {
font-size: 14px;
font-weight: 400;
text-transform: none;
color: #000000;
letter-spacing: 0;
display: block;
width: 170px;
padding: 11px 10px 11px 20px;
text-align: left;
}

#aperturemenu ul li ul li:hover>a,
#aperturemenu ul li ul li.active>a {
color: #777;
}

#aperturemenu ul li ul li:hover:after,
#aperturemenu ul li ul li.active:after {
background: #eee;
}

#aperturemenu ul li ul li:hover>ul {
left: 100%;
top: 0;
}

#aperturemenu ul li ul li:hover>ul>li {
max-height: 72px;
position: relative;
}

#aperturemenu>ul>li {
display: inline-block;
}

#aperturemenu.align-center>ul>li {
float: none;
display: inline-block;
}

#aperturemenu.align-center>ul {
text-align: center;
}

#aperturemenu.align-center ul ul {
text-align: left;
}

#aperturemenu.align-right>ul {
float: right;
}

#aperturemenu.align-right>ul>li:hover>ul {
left: auto;
right: 0;
}

#aperturemenu.align-right ul ul li:hover>ul {
right: 100%;
left: auto;
}

#aperturemenu.align-right ul ul li a {
text-align: right;
}

#aperturemenu>ul>li:after {
content: '';
display: block;
position: absolute;
width: 100%;
height: 1px;
top: 0;
z-index: 0;
border-bottom: 1px solid #000
background: transparent;
-webkit-transition: border-bottom .2s;
-moz-transition: border-bottom .2s;
-ms-transition: border-bottom .2s;
-o-transition: border-bottom .2s;
transition: border-bottom .2s;
}

#aperturemenu>ul>li.has-sub>a {
padding-right: 40px;
}

#aperturemenu>ul>li.has-sub>a:after {
display: block;
content: '';
background: transparent;
height: 12px;
width: 12px;
position: absolute;
border-radius: 13px;
right: 14px;
top: 16px;
}

#aperturemenu>ul>li.has-sub>a:before {
display: block;
content: '';
border: 4px solid transparent;
border-top-color: #777;
z-index: 2;
height: 0;
width: 0;
position: absolute;
right: 16px;
top: 21px;
}

#aperturemenu>ul>li>a {
color: #000;
padding:15px;
text-transform: capitalize;
font-size: 14px;
font-weight: 300;
z-index: 2;
position: relative;
}

#aperturemenu>ul>li:hover:after,
#aperturemenu>ul>li.active:after {
height: 100%;
}

#aperturemenu>ul>li:hover>a,
#aperturemenu>ul>li.active>a {
color: #000000;
}

#aperturemenu>ul>li:hover>a:after,
#aperturemenu>ul>li.active>a:after {
}

#aperturemenu>ul>li:hover>a:before,
#aperturemenu>ul>li.active>a:before {
border-top-color: #eee;
}

#aperturemenu>ul>li:hover>ul {
left: 0;
}

#aperturemenu>ul>li:hover>ul>li {
max-height: 72px;
position: relative;
}

#aperturemenu #menu-button {
display: none;
}

#aperturemenu>ul>li>a {
width: auto;
display: inline-block;
}

#aperturemenu>ul>li {
width: auto;
}

#aperturemenu>ul>li>ul {
width: 170px;
display: block;
}

#aperturemenu>ul>li>ul>li {
width: 170px;
display: block;
}

@media all and (max-width: 800px),
only screen and (-webkit-min-device-pixel-ratio: 2) and (max-width: 1024px),
only screen and (min--moz-device-pixel-ratio: 2) and (max-width: 1024px),
only screen and (-o-min-device-pixel-ratio: 2/1) and (max-width: 1024px),
only screen and (min-device-pixel-ratio: 2) and (max-width: 1024px),
only screen and (min-resolution: 192dpi) and (max-width: 1024px),
only screen and (min-resolution: 2dppx) and (max-width: 1024px) {
#aperturemenu>ul {
max-height: 0;
overflow: hidden;
-webkit-transition: max-height 0.35s ease-out;
-moz-transition: max-height 0.35s ease-out;
-ms-transition: max-height 0.35s ease-out;
-o-transition: max-height 0.35s ease-out;
transition: max-height 0.35s ease-out;
}
#aperturemenu>ul>li>ul {
width: 100%;
display: block;
}
#aperturemenu.align-right ul li a {
text-align: left;
}
#aperturemenu>ul>li>ul>li {
width: 100%;
display: block;
}
#aperturemenu.align-right ul ul li a {
text-align: left;
}
#aperturemenu>ul>li>ul>li>a {
width: 100%;
display: block;
}
#aperturemenu ul li ul li a {
width: 100%;
}
#aperturemenu.align-center>ul {
text-align: left;
}
#aperturemenu.align-center>ul>li {
display: block;
}
#aperturemenu>ul.open {
max-height: 1000px;
border-top: 1px solid rgba(110, 110, 110, 0.25);
}
#aperturemenu ul {
width: 100%;
}
#aperturemenu ul>li {
float: none;
width: 100%;
}
#aperturemenu ul li a {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
padding: 12px 20px;
}
#aperturemenu ul>li:after {
display: none;
}
#aperturemenu ul li.has-sub>a:after,
#aperturemenu ul li.has-sub>a:before,
#aperturemenu ul li ul li.has-sub:after,
#aperturemenu ul li ul li.has-sub:before {
display: none;
}
#aperturemenu ul li ul,
#aperturemenu ul li ul li ul,
#aperturemenu ul li ul li:hover>ul,
#aperturemenu.align-right ul li ul,
#aperturemenu.align-right ul li ul li ul,
#aperturemenu.align-right ul li ul li:hover>ul {
left: 0;
position: relative;
right: auto;
}
#aperturemenu ul li ul li,
#aperturemenu ul li:hover>ul>li {
max-height: 999px;
position: relative;
background: none;
}
#aperturemenu ul li ul li a {
padding: 8px 20px 8px 35px;
color: #333;
}
#aperturemenu ul li ul ul li a {
padding: 8px 20px 8px 50px;
}
#aperturemenu ul li ul li:hover>a {
color: #000000;
}
#aperturemenu #menu-button {
display: block;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
padding: 15px 20px;
text-transform: uppercase;
font-weight: 700;
font-size: 14px;
letter-spacing: 1px;
color: #000;
cursor: pointer;
border: 1px solid #333;
}
#aperturemenu #menu-button:after {
display: block;
content: '';
position: absolute;
height: 3px;
width: 22px;
border-top: 2px solid #333;
border-bottom: 2px solid #333;
right: 20px;
top: 16px;
}
#aperturemenu #menu-button:before {
display: block;
content: '';
position: absolute;
height: 3px;
width: 22px;
border-top: 2px solid #333;
border-bottom: 2px solid #333;
right: 20px;
top: 26px;
}
}

 

I’ve been using the code above on all our themes, with changes mostly in the CSS only. There are several ways of building a responsive menu, this is just one of them. If you have any questions or want to make improvements, let me know below.

Published by denis

Leave a Reply

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

Error: Please enter a valid email address

Error: Invalid email

Error: Please enter your first name

Error: Please enter your last name

Error: Please enter a username

Error: Please enter a password

Error: Please confirm your password

Error: Password and password confirmation do not match