Skip to content

Vertical Accordion Menu

By GRAYBOX Alumni

Vertical menu's have recently become the newest rave in the Web industry. They are responsive, save space, and look great on any device.

Let's get things started by putting in place our HTML and add the underlying structure to the menu:

Vertical Navigation Menu: CSS3 Coded

[code] 

[/code]

Now let's create the submenus by placing an unordered list nested within each of our existing list items.

[code] 

[/code]

I've also added a class to each list item, this will just make styling easier later on. Of course, if you are using a smart content management system this whole navigation generation would be automatic :) For the numbers we have created a span tag inside each anchor tag (this will make sense in just a second).

Making Our Fonts Fluid

We'll first make sure our menu displays correctly which is not by default. Add these rules to css/styles.css, they'll set the margin and padding of all our uls to 0, and remove the list style. To automatically do this for every stylesheet i recommend using a good CSS reset like [code] ul, ul ul { margin: 0; padding: 0; list-style: none; } [/code]

A" class="redactor-autoparser-object">http://meyerweb.com/eric/tools... Small Lesson on em Fonts

Before we start styling our menu we'll create a wrapper with a fixed width and a font-size of 13px (expressed in em units). First, we'll add a rule to the body, font-size:100%;. This ensures that our styling is based on the browser default font-size (typically 16px).

Now to explain how the wrapper font size actually works. We need to express it as an em; proportional to the size of its parent's font-size. We're aiming for a font-size of 13px, so assuming the parent size is 16px, our resultant em is 13 / 16 = 0.8125. 13px is 0.8125*16px.

Measuring our fonts (and other elements) in em units will in turn make them fluid. Now if we change the wrapper font size (or our browser default size) the whole menu will adjust in relation to that base. If this isn't making sense to you, I suggest you visit pxtoem.com where they have a pixel size converter.

[code] body { font-size: 100%; } a { text-decoration: none; } ul, ul ul { margin: 0; padding: 0; list-style: none; } #wrapper { width: 220px; margin: 100px auto; font-size: 0.8125em; } [/code] 

We've applied a fixed width to the wrapper class and centered it using the auto property of margin.

Let's add some styling for the menu and make the width and height of the menu ul auto, then apply a shadow to the whole thing. By adding the height as auto, the shadow will now adjust when the slider opens.

Then the anchor tags; we need to add a width of 100% so they will stretch to the 220px width of the wrapper making the whole row clickable and not just the text. For a height we'll use ems, so think back to our main font-size of 13px. Our design we are following shows a height of 36px, so that's our target. We'll take 36 and divide it by 13 which comes out to roughly 2.75em (36 / 13 = 2.76923077). We'll also use 2.75em for the line height (to center all text vertically) then apply some text-indent to push text in, making space for icons which we can add at a later time.

We've added a CSS3 gradient for the background. Next we need to change the font, and apply the Helvetica Neue font and a white color along with a text shadow. We did not need to specify a font-size because our base font of 13px for the wrapper was inherited.

Here is what you should have so far:

[code] body { font-size: 100%; background:#32373d; } a { text-decoration: none; } ul, ul ul { margin: 0; padding: 0; list-style: none; } #wrapper { width: 220px; margin: 100px auto; font-size: 0.8125em; } .menu { width: auto; height: auto; -webkit-box-shadow: 0px 1px 3px 0px rgba(0,0,0,.73), 0px 0px 18px 0px rgba(0,0,0,.13); -moz-box-shadow: 0px 1px 3px 0px rgba(0,0,0,.73), 0px 0px 18px 0px rgba(0,0,0,.13); box-shadow: 0px 1px 3px 0px rgba(0,0,0,.73), 0px 0px 18px 0px rgba(0,0,0,.13); } .menu > li > a { background-color: #616975; background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(114, 122, 134)),to(rgb(80, 88, 100))); background-image: -webkit-linear-gradient(top, rgb(114, 122, 134), rgb(80, 88, 100)); background-image: -moz-linear-gradient(top, rgb(114, 122, 134), rgb(80, 88, 100)); background-image: -o-linear-gradient(top, rgb(114, 122, 134), rgb(80, 88, 100)); background-image: -ms-linear-gradient(top, rgb(114, 122, 134), rgb(80, 88, 100)); background-image: linear-gradient(top, rgb(114, 122, 134), rgb(80, 88, 100)); filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#727a86', EndColorStr='#505864'); border-bottom: 1px solid #33373d; -webkit-box-shadow: inset 0px 1px 0px 0px #878e98; -moz-box-shadow: inset 0px 1px 0px 0px #878e98; box-shadow: inset 0px 1px 0px 0px #878e98; width: 100%; height: 2.75em; line-height: 2.75em; text-indent: 2.75em; display: block; position: relative; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 600; color: #fff; text-shadow: 0px 1px 0px rgba(0,0,0,.5); } [/code] 

Tip: Commenting EM Values

From the CSS above you can see that it's easy to get lost on em values. I have found that It's generally a good idea to leave comments of your original calculations, so that when you return to your code to make changes three months from now you can tell exactly how you reached that value. Remember the formula: desired px / parent px = resultant em and use the approximate symbol (≈) if you're rounding the result. As a general note, it's good practice to comment on your code as much as possible so that not only you can better understand your processes, but it makes your code more accessible for those who need to interact with it.

Let's go ahead and add those comments in now where need be:

[code] #wrapper { font-size: 0.8125em; /* 13/16 = 0.8125*/ } .menu > li > a { height: 2.75em; /* 36/13 ≈ 2.75*/ line-height: 2.75em; /* 36/13 ≈ 2.75*/ text-indent: 2.75em; /* 36/13 ≈ 2.75*/ } [/code] 

Time to add some CSS for the white sub menus. Note we used display:block. If we'd used float:left the menus wouldn't smoothly animate, so we use display block to help them move nice and smoothly. Go ahead and try floating them left and you will see what i am talking about. You will also notice we've added an extra style; we're applying this to the last child of the sub-ul. We need to do this so we're able to change the border color.

[code] .menu ul li a { background: #fff; border-bottom: 1px solid #efeff0; width: 100%; height: 2.75em; line-height: 2.75em; text-indent: 2.75em; display: block; position: relative; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 0.923em; font-weight: 400; color: #878d95; } .menu ul li:last-child a { border-bottom: 1px solid #33373d; } [/code] 

Hover and Active styles

Now if you're wondering when we are going to add the active class that's what the jQuery we will be adding later will take care of. Again, implementing this menu in a situation where a solid CMS (like ExpressionEngine) is installed would make much of these processes irrelevant.

[code] .menu > li > a:hover, .menu > li > a.active { background-color: #35afe3; background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(69, 199, 235)),to(rgb(38, 152, 219))); background-image: -webkit-linear-gradient(top, rgb(69, 199, 235), rgb(38, 152, 219)); background-image: -moz-linear-gradient(top, rgb(69, 199, 235), rgb(38, 152, 219)); background-image: -o-linear-gradient(top, rgb(69, 199, 235), rgb(38, 152, 219)); background-image: -ms-linear-gradient(top, rgb(69, 199, 235), rgb(38, 152, 219)); background-image: linear-gradient(top, rgb(69, 199, 235), rgb(38, 152, 219)); filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#45c7eb', EndColorStr='#2698db'); border-bottom: 1px solid #103c56; -webkit-box-shadow: inset 0px 1px 0px 0px #6ad2ef; -moz-box-shadow: inset 0px 1px 0px 0px #6ad2ef; box-shadow: inset 0px 1px 0px 0px #6ad2ef; } .menu > li > a.active { [/code] 

Numbers

Okay, remember those spans we added? These will create the numbers! Once again ‚ em to make this fluid.

Once again to make it fluid, we'll use padding to create the width and height. We've even used ems on the border radius; we'll need this because if the text is made bigger they'll appear disproportionate. I've also added another style for when hovering or an active class is applied to the link.

[code] .menu > li > a span { font-size: 0.857em; display: inline-block; position: absolute; right: 1em; top: 50%; background: #48515c; line-height: 1em; height: 1em; padding: .4em .6em; margin: -.8em 0 0 0; color: #fff; text-indent: 0; text-align: center; -webkit-border-radius: .769em; -moz-border-radius: .769em; border-radius: .769em; -webkit-box-shadow: inset 0px 1px 3px 0px rgba(0, 0, 0, .26), 0px 1px 0px 0px rgba(255, 255, 255, .15); -moz-box-shadow: inset 0px 1px 3px 0px rgba(0, 0, 0, .26), 0px 1px 0px 0px rgba(255, 255, 255, .15); box-shadow: inset 0px 1px 3px 0px rgba(0, 0, 0, .26), 0px 1px 0px 0px rgba(255, 255, 255, .15); text-shadow: 0px 1px 0px rgba(0,0,0,.5); font-weight: 500; } .menu > li > a:hover span, .menu > li a.active span { background: #2173a1; } [/code] 

Sub Menu Arrows and Arrows

We need to add that arrow and will once again lean on the :before psuedo. We define a width and height and add some left positioning using ems to ensure it is fluid.

[code] .menu ul > li > a span { font-size: 0.857em; display: inline-block; position: absolute; right: 1em; top: 50%; / background: #fff; border: 1px solid #d0d0d3; line-height: 1em; height: 1em; padding: .4em .7em; margin: -.9em 0 0 0; color: #878d95; text-indent: 0; text-align: center; -webkit-border-radius: .769em; -moz-border-radius: 769em; border-radius: 769em; text-shadow: 0px 0px 0px rgba(255,255,255,.01)); } .menu > li > ul li a:before { content: '‚ñ∂'; font-size: 8px; color: #bcbcbf; position: absolute; width: 1em; height: 1em; top: 0; left: -2.7em; } .menu > li > ul li:hover a, .menu > li > ul li:hover a span, .menu > li > ul li:hover a:before { color: #32373D; } [/code] 

Last Step: jQuery

So I will just point out some main components of this script and leave the rest up to you:

First we're storing the sub-menu and the main menu anchor tags in two different variables, this just makes it easy to refer to them later on. Hide the sub-menu on load We tell it to do something when we click one of the main menu's anchor tags. We disable anchor tags. Now we'll instruct it that IF the menu_a has the class ‘active', remove it. We use ‘.filter' and ‘:visible'. If a menu is open, slide it up with a speed of normal. If the menu is closed, add the class ‘active' (so we can access the nice CSS style) and slide it down with a speed of normal. Now, we'll need to use an ELSE as part of our conditional statement. So, ELSE remove the class active, and slide the menu up to hide it. This is just so we can code any menu without having to reload the page.

Note: You can change the speed of slide, just change ‘normal' to your preferred integer, e.g. '500′. That will slide it at 500 milliseconds.

Result

So here we have it! We are done, and it looks great! Now, the best part about this entire menu is it's responsive property. All we have to do is change the width of it's parent wrapper and the entire menu auto adjusts (see example 2)!

See the Pen Accordion Menu by Kevin Carpenter (@KevinCarpenter) on CodePen

See the Pen Accordion #2 Wider by Kevin Carpenter (@KevinCarpenter) on CodePen

Blog & Events

Featured Work