Fall Sale! Code FALL2024 takes 25% OFF our Pro Plugins & Books »
Web Dev + WordPress + Security

CSS Dropdown Menu in WordPress

In this tutorial I am going to show you how to build a pure CSS drop down menu in WordPress. I will walk you through the steps of creating a menu in WordPress, customizing it with CSS, and then printing the menu in your theme file. This tutorial requires that you have access to edit your WordPress theme files and also a basic understanding of HTML and CSS. I will walk through the process step-by-step so don’t worry if you have never edited a WP theme file before.

Note: I will be using WordPress 3.9 and the Twenty Thirteen theme for this tutorial, but all the steps should be the same for any WordPress 3 website.

Demo

Here is a live demo showing what our menu will look like when we are finished with this tutorial.

Source Files

Jump to Downloads for a zip file that contains all the necessary code for this tutorial.

Create a Menu in WordPress

Chances are you already have a menu built in WordPress. WordPress 3 has an easy to use, drag-n-drop interface for you to create a menu structure. If you haven’t already, go to Appearance > Menus to build and save a custom menu. Make sure to give your menu a name and remember it for later.

WordPress Admin Area - Appearance - Menus

Add a Custom Walker Class to functions.php

Before we can print our menu in a WordPress theme file, we need to add a WordPress Walker Class to our functions.php file. This piece of code will clean up the HTML output of our menu so that we can more easily create the CSS styles. Simply copy and paste the Walker code into your functions.php file which should be right inside your theme folder. If a functions.php file does not exist, create it and then paste the Walker Class 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 Menu in a Theme File

We now have a menu built and our Walker Class is in place. It’s now time to print our menu inside a theme file. To do this we will be using the wp_nav_menu() function. Using PHP we will call this function and pass it 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 back in step 1. The container_id will add a CSS ID to the menu HTML which we will use in our CSS styles later. 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 Menu', 
	'container_id' => 'cssmenu', 
	'walker' => new CSS_Menu_Walker()
)); ?>

Place this PHP code in one of your theme files. Where ever you place it is where your menu will be printed out. I am using the default WordPress Twenty Thirteen theme so there is a theme file called header.php which is where I will place the code.

If you have done everything correctly up to this point you should see an unstyled HTML list being displayed in your theme. If you are not seeing your menu printed out, double check the previous steps.

WordPress Menu displayed without any CSS

Add the CSS

Now it’s time to add the styles to our menu and see the fruits of our labor. Go ahead and open up the CSS file for your theme. It might be in a folder called css/styles.css or just a file in the root directory of the theme. You can copy and paste all the CSS below into your theme’s CSS file at once if you want. I will be breaking the CSS up into parts in order to explain it more easily.

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

This first bit of CSS is just some simple resets to make sure that each browsers is starting from the same point.

#cssmenu,
#cssmenu ul,
#cssmenu li,
#cssmenu a {
	border: none;
	margin: 0;
	padding: 0;
	line-height: 1;
	-webkit-box-sizing: content-box;
	-moz-box-sizing: content-box;
	box-sizing: content-box;
}

This next bit of CSS will be styling the first level of our drop down menu. One thing to note about this code is the CSS3 gradients that we are using for the background. If the user’s browser doesn’t support gradients, then we just fill in the background with the color #3c3c3c.

Another style worth noting is the use of the :after pseudo class. This lets us add the faint border colors around each menu item which give the design some depth.

#cssmenu {
	height: 37px;
	display: block;
	padding: 0;
	margin: 0;
	border: 1px solid;
	border-radius: 5px;
	width: auto;
	border-color: #080808;
}
#cssmenu,
#cssmenu > ul > li > ul > li a:hover {
	background: #3c3c3c;
	background: -moz-linear-gradient(top, #3c3c3c 0%, #222222 100%);
	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #3c3c3c), color-stop(100%, #222222));
	background: -webkit-linear-gradient(top, #3c3c3c 0%, #222222 100%);
	background: -o-linear-gradient(top, #3c3c3c 0%, #222222 100%);
	background: -ms-linear-gradient(top, #3c3c3c 0%, #222222 100%);
	background: linear-gradient(top, #3c3c3c 0%, #222222 100%);
}
#cssmenu > ul {
	list-style: inside none;
	padding: 0;
	margin: 0;
}
#cssmenu > ul > li {
	list-style: inside none;
	padding: 0;
	margin: 0;
	float: left;
	display: block;
	position: relative;
}
#cssmenu > ul > li > a {
	outline: none;
	display: block;
	position: relative;
	padding: 12px 20px;
	text-align: center;
	text-decoration: none;
	text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.4);
	font-weight: bold;
	font-size: 13px;
	font-family: Arial, Helvetica, sans-serif;
	border-right: 1px solid #080808;
	color: #ffffff;  
}
#cssmenu > ul > li > a:hover {
	background: #080808;
	color: #ffffff;
}
#cssmenu > ul > li:first-child > a {
	border-radius: 5px 0 0 5px;
}
#cssmenu > ul > li > a:after {
	content: '';
	position: absolute;
	border-right: 1px solid;
	top: -1px;
	bottom: -1px;
	right: -2px;
	z-index: 99;
	border-color: #3c3c3c;  
}

At this point your menu should look something like the image below. If it doesn’t, circle back and double check all the CSS is correct.

WordPress Menu displayed with CSS for the parent menu

Next we need to style the sub menus. We will want them to be hidden by default and then shown when the user hovers over the parent item. This can be accomplished using pure CSS.

First we will want to set all the sub menu UL to display: none;. Then we will use the :hover pseudo class on the sub menu to display: block;. This has the effect of hiding and showing the sub menu when the user hovers over a parent item.

#cssmenu ul li.has-sub:hover > a:after {
	top: 0;
	bottom: 0;
}
#cssmenu > ul > li.has-sub > a:before {
	content: '';
	position: absolute;
	top: 18px;
	right: 6px;
	border: 5px solid transparent;
	border-top: 5px solid #ffffff;
}
#cssmenu > ul > li.has-sub:hover > a:before {
	top: 19px;
}
#cssmenu ul li.has-sub:hover > a {
	background: #3f3f3f;
	border-color: #3f3f3f;
	padding-bottom: 13px;
	padding-top: 13px;
	top: -1px;
	z-index: 999;
}
#cssmenu ul li.has-sub:hover > ul,
#cssmenu ul li.has-sub:hover > div {
	display: block;
}
#cssmenu ul li.has-sub > a:hover {
	background: #3f3f3f;
	border-color: #3f3f3f;
}
#cssmenu ul li > ul,
#cssmenu ul li > div {
	display: none;
	width: auto;
	position: absolute;
	top: 38px;
	padding: 10px 0;
	background: #3f3f3f;
	border-radius: 0 0 5px 5px;
	z-index: 999;
}
#cssmenu ul li > ul {
	width: 200px;
}
#cssmenu ul li > ul li {
	display: block;
	list-style: inside none;
	padding: 0;
	margin: 0;
	position: relative;
}
#cssmenu ul li > ul li a {
	outline: none;
	display: block;
	position: relative;
	margin: 0;
	padding: 8px 20px;
	font: 10pt Arial, Helvetica, sans-serif;
	color: #ffffff;
	text-decoration: none;
	text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.5);
}
#cssmenu ul ul a:hover {
	color: #ffffff;
}
#cssmenu > ul > li.has-sub > a:hover:before {
	border-top: 5px solid #ffffff;
}

That’s it! If you did everything correctly you should have brand new WordPress drop down menu. If things don’t seem right check out the troubleshooting below.

Finished Product

Here again is a live demo showing what our menu will look like when we are finished with this tutorial.

Download Files

Grab the source files here (zip file contains functions.php and styles.css):

Download CSS Dropdown MenuVersion 1.0 ( 1.88 KB ZIP )

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.

About the Author
Russell Martin is professional web developer and entrepreneur based out of California with over 10 years experience in the industry. His latest project is CSS Menu Maker which provides free drop down menu code and a Wordpress Menu Plugin for the Wordpress community.
SAC Pro: Unlimited chats.

6 responses to “CSS Dropdown Menu in WordPress”

  1. rajat gupta 2014/08/15 2:28 am

    very nice menus. i have downloaded the file. will use it and revert with feedback soon.

  2. Really handy CSS to add dropdown menu without plugin. Will it possible to add mega menu with Pure CSS?

  3. Very nice tutorial!
    But I think there is a little mistake in your code. I didn’t download it. I used the code step by step from the side. And in the code in the function.php the class is called CSS_Menu_Maker_Walker and in the next snippet only CSS_Menu_Walker().
    I changed it in my code and now it workes fine! =)

  4. Bill Wynne 2014/09/07 8:28 pm

    I am surely going to download it and give it a try. Some of my older blogs don’t have menus at all and I hope this works well. I’ll try to come back with an update.

  5. how to change the size? now is full wide i want to make it smaller i tried by:

    #cssmenu { height: 37px; display: block; padding: 0; margin: 0px; border: 1px solid; border-radius: 5px; width: auto; border-color: #080808; }

    padding AND margin but had effect of all sides just i want from left and right side get space?

  6. Your “Print Menu in a Theme File” is wrong:

    'Main Menu', 'container_id' => 'cssmenu', 'walker' => new CSS_Menu_Walker() )); ?>

    Should read:

    'Main Menu', 'container_id' => 'cssmenu', 'walker' => new CSS_Menu_Maker_Walker() )); ?>

    You left out the “Maker” in defining ‘walker’

Comments are closed for this post. Something to add? Let me know.
Welcome
Perishable Press is operated by Jeff Starr, a professional web developer and book author with two decades of experience. Here you will find posts about web development, WordPress, security, and more »
Digging Into WordPress: Take your WordPress skills to the next level.
Thoughts
I disabled AI in Google search results. It was making me lazy.
Went out walking today and soaked up some sunshine. It felt good.
I have an original box/packaging for 2010 iMac if anyone wants it free let me know.
Always ask AI to cite its sources. Also: “The Web” is not a valid answer.
All free plugins updated and ready for WP 6.6 dropping next week. Pro plugin updates in the works also complete :)
99% of video thumbnail/previews are pure cringe. Goofy faces = Clickbait.
RIP ICQ
Newsletter
Get news, updates, deals & tips via email.
Email kept private. Easy unsubscribe anytime.