WordPress Responsive Drop Down Menu Tutorial

Today I will be walking you through how to create a custom drop down menu in WordPress. We will be using the WordPress menu system to control the structure of the menu and then applying our own custom styles to the WordPress theme.

The menu I will be explaining is fully responsive and works with touch devices, so there is a little bit of jQuery code needed to make sure everything is cross browser compatible.

Demo

Here is what our menu is going to look like when we are finished. Resize the screen to see what the responsive version looks like.

View Demo

Requirements

  • Ability to edit your theme’s functions.php file
  • Ability to upload files to your theme’s directory.
  • Basic understanding of CSS and PHP

Create a Menu in WordPress

Before we can start styling our menu, we need to first create the HTLM structure using the WordPress menu system. You probably already have a menu built in WordPress, but if you don’t head over to Appearance > Menus and create one. WordPress provides a easy to use interface to allows you to drag and drop menu items into a hierarchy. When you are done creating your menu structure, give the menu a name and then save it.

Wordpress Menu System

A Custom Walker Class

Before adding our CSS and jQuery to the menu, we first need to make sure that the HTML being outputted for the menu is nice and clean. I’ve created a custom WordPress Walker Class that does just that. The WordPress Walker Class takes the structure of your menu and then prints out the corresponding HTML. The Walker class below makes sure we have nice, clean HTML to work with and that our menu items have the correct CSS classes assigned to them.

To use this Walker class, simply copy and paste it to the bottom of your theme’s functions.php file. If you don’t see a functions.php file, then create one and paste the code in.

class CSS_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";
  }
}  

Print the Menu

Now we need to print out our menu using the wp_nav_menu() function. Where ever you place this next bit of code is where you menu will be printed out. I’m using one of the default themes so I’m printing my menu in the header.php file. This will be different for you though depending on where you want your menu to show up.

Using PHP we will pass this function the parameters of our menu so that it can print out the HTML structure. The ‘menu’ parameter is the name of the menu you created in WordPress. The ‘container_id’ will add an ID to the menu’s containing DIV. The ‘Walker’ parameter is telling the wp_nav_menu() function to use our custom WordPress walker class to print the HTML.

<?php 
  wp_nav_menu(array(
    'menu' => 'Main Navigation', 
    'container_id' => 'cssmenu', 
    'walker' => new CSS_Menu_Walker()
  )); 
?> 

Once you have placed this code in your theme’s template file, save the file and then refresh your site in the browsers. We have not added any styles or jQuery so you should see a plain HTML list being printed out like the one below.

wordpress_2

The CSS

Now we need to add the CSS styles to our menu so open up your themes CSS file. It might be in a folder called css/styles.css or just a file in the root directory of the theme. I’ll break up the CSS to explain it a little bit, but you can also skip ahead and just copy and paste all the code in at one time.

Notice that we are using CSS ID that we specified in the wp_nav_menu() function, #cssmenu.

This first bit of CSS takes care of the browser resets so that everything is uniform. We are also using the free Google Font PT Sans that we are importing at the top of our file.

@import url(https://fonts.googleapis.com/css?family=PT+Sans:400,700);
#cssmenu {
  background: #4cb6ea;
  margin: 0;
  width: auto;
  padding: 0;
  line-height: 1;
  display: block;
  position: relative;
  font-family: 'PT Sans', sans-serif;
  -webkit-box-sizing: content-box;
  -moz-box-sizing: content-box;
  box-sizing: content-box;  
}

This next chunk of CSS will take care of the majority of our menu’s styles. I wont go over it all in detail, feel free to ask questions in the comments about any part that you don’t understand.

#cssmenu ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: block;
}
#cssmenu ul:after,
#cssmenu:after {
  content: ' ';
  display: block;
  font-size: 0;
  height: 0;
  clear: both;
  visibility: hidden;
}
#cssmenu ul li {
  margin: 0;
  padding: 0;
  display: block;
  position: relative;
}
#cssmenu 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;
}
#cssmenu ul li ul {
  position: absolute;
  left: -9999px;
  top: auto;
}
#cssmenu 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;
}
#cssmenu 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;
}
#cssmenu.align-right ul li ul li.has-sub:after {
  right: auto;
  left: 15px;
}
#cssmenu 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;
}
#cssmenu.align-right ul li ul li.has-sub:before {
  right: auto;
  left: 15px;
  border-left-color: transparent;
  border-right-color: #ffffff;
}
#cssmenu 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;
}
#cssmenu ul li ul li:hover > a,
#cssmenu ul li ul li.active > a {
  color: #4cb6ea;
}
#cssmenu ul li ul li:hover:after,
#cssmenu ul li ul li.active:after {
  background: #4cb6ea;
}
#cssmenu ul li ul li:hover > ul {
  left: 100%;
  top: 0;
}
#cssmenu ul li ul li:hover > ul > li {
  max-height: 72px;
  position: relative;
}
#cssmenu > ul > li {
  float: left;
}
#cssmenu.align-center > ul > li {
  float: none;
  display: inline-block;
}
#cssmenu.align-center > ul {
  text-align: center;
}
#cssmenu.align-center ul ul {
  text-align: left;
}
#cssmenu.align-right > ul {
  float: right;
}
#cssmenu.align-right > ul > li:hover > ul {
  left: auto;
  right: 0;
}
#cssmenu.align-right ul ul li:hover > ul {
  right: 100%;
  left: auto;
}
#cssmenu.align-right ul ul li a {
  text-align: right;
}
#cssmenu > ul > li:after {
  content: '';
  display: block;
  position: absolute;
  width: 100%;
  height: 0;
  top: 0;
  z-index: 0;
  background: #ffffff;
  -webkit-transition: height .2s;
  -moz-transition: height .2s;
  -ms-transition: height .2s;
  -o-transition: height .2s;
  transition: height .2s;
}
#cssmenu > ul > li.has-sub > a {
  padding-right: 40px;
}
#cssmenu > ul > li.has-sub > a:after {
  display: block;
  content: '';
  background: #ffffff;
  height: 12px;
  width: 12px;
  position: absolute;
  border-radius: 13px;
  right: 14px;
  top: 16px;
}
#cssmenu > ul > li.has-sub > a:before {
  display: block;
  content: '';
  border: 4px solid transparent;
  border-top-color: #4cb6ea;
  z-index: 2;
  height: 0;
  width: 0;
  position: absolute;
  right: 16px;
  top: 21px;
}
#cssmenu > ul > li > a {
  color: #ffffff;
  padding: 15px 20px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  font-size: 14px;
  z-index: 2;
  position: relative;
}
#cssmenu > ul > li:hover:after,
#cssmenu > ul > li.active:after {
  height: 100%;
}
#cssmenu > ul > li:hover > a,
#cssmenu > ul > li.active > a {
  color: #000000;
}
#cssmenu > ul > li:hover > a:after,
#cssmenu > ul > li.active > a:after {
  background: #000000;
}
#cssmenu > ul > li:hover > a:before,
#cssmenu > ul > li.active > a:before {
  border-top-color: #ffffff;
}
#cssmenu > ul > li:hover > ul {
  left: 0;
}
#cssmenu > ul > li:hover > ul > li {
  max-height: 72px;
  position: relative;
}
#cssmenu #menu-button {
  display: none;
}
#cssmenu > ul > li > a {
  display: block;
}
#cssmenu > ul > li {
  width: auto;
}
#cssmenu > ul > li > ul {
  width: 170px;
  display: block;
}
#cssmenu > ul > li > ul > li {
  width: 170px;
  display: block;
}

If everything has been done correctly, your should have a completely functioning drop down menu. If your menu doesn’t look like the image below, go back and double check your CSS styles.

Wordpress Drop Down Menu

Now that we have your basic menu styles in place we need to create the CSS styles that will take effect for mobile devices. This is the responsive part of the tutorial. We will be using Media Queries to specify which styles kick in and at what screen width.

@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) {
  #cssmenu > 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;
  }
  #cssmenu > ul > li > ul {
    width: 100%;
    display: block;
  }
  #cssmenu.align-right ul li a {
    text-align: left;
  }
  #cssmenu > ul > li > ul > li {
    width: 100%;
    display: block;
  }
  #cssmenu.align-right ul ul li a {
    text-align: left;
  }
  #cssmenu > ul > li > ul > li > a {
    width: 100%;
    display: block;
  }
  #cssmenu ul li ul li a {
    width: 100%;
  }
  #cssmenu.align-center > ul {
    text-align: left;
  }
  #cssmenu.align-center > ul > li {
    display: block;
  }
  #cssmenu > ul.open {
    max-height: 1000px;
    border-top: 1px solid rgba(110, 110, 110, 0.25);
  }
  #cssmenu ul {
    width: 100%;
  }
  #cssmenu ul > li {
    float: none;
    width: 100%;
  }
  #cssmenu ul li a {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
    width: 100%;
    padding: 12px 20px;
  }
  #cssmenu ul > li:after {
    display: none;
  }
  #cssmenu ul li.has-sub > a:after,
  #cssmenu ul li.has-sub > a:before,
  #cssmenu ul li ul li.has-sub:after,
  #cssmenu ul li ul li.has-sub:before {
    display: none;
  }
  #cssmenu ul li ul,
  #cssmenu ul li ul li ul,
  #cssmenu ul li ul li:hover > ul,
  #cssmenu.align-right ul li ul,
  #cssmenu.align-right ul li ul li ul,
  #cssmenu.align-right ul li ul li:hover > ul {
    left: 0;
    position: relative;
    right: auto;
  }
  #cssmenu ul li ul li,
  #cssmenu ul li:hover > ul > li {
    max-height: 999px;
    position: relative;
    background: none;
  }
  #cssmenu ul li ul li a {
    padding: 8px 20px 8px 35px;
    color: #ffffff;
  }
  #cssmenu ul li ul ul li a {
    padding: 8px 20px 8px 50px;
  }
  #cssmenu ul li ul li:hover > a {
    color: #000000;
  }
  #cssmenu #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: #ffffff;
    cursor: pointer;
  }
  #cssmenu #menu-button:after {
    display: block;
    content: '';
    position: absolute;
    height: 3px;
    width: 22px;
    border-top: 2px solid #ffffff;
    border-bottom: 2px solid #ffffff;
    right: 20px;
    top: 16px;
  }
  #cssmenu #menu-button:before {
    display: block;
    content: '';
    position: absolute;
    height: 3px;
    width: 22px;
    border-top: 2px solid #ffffff;
    right: 20px;
    top: 26px;
  }
}

We wont be able to see our responsive styles until we add a little bit of jQuery. So lets do that first before checking our menu.

Minimal jQuery

Next we will need to add the small amount of jQuery below to one of our themes script files. Almost all WordPress themes use jQuery these days so just find the file called script.js or possibly functions.js and paste the code in. Notice we are using the #cssmenu ID again to target our menu.

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

Test it Out

Save all your files and go back to your browser. To test the responsive styles, resize your browser window to less than 800px. Your menu should look like the image below:

wordpress_4

That’s it! If everything was done correctly you should have a functioning WordPress responsive drop down menu.

Troubleshooting

If your menu doesn’t seem to be function correctly, or it looks a little off then it is possible you are experiencing residual styling from your WordPress theme. This means that there are other CSS styles in your theme that are conflicting with our menu styles. The only way to fix this problem is to track down the other CSS styles and remove them. Your best bet is to use the Firebug extension for Firefox and inspect the CSS styles manually. Once you find the conflicting styles remove or delete them.


WordPress › Error

There has been a critical error on this website.

Learn more about troubleshooting WordPress.