Mega Menu for SharePoint – Part 1 of 3

In this three part series, Heather Solomon and Dustin Miller will explore the ever-popular “Mega Menu”, and how to create a powerful, styled and functional mega menu for use on your SharePoint sites. In this first part, the focus is on the HTML markup and CSS styling to used to create this oft-requested UI element.

Please note this article is cross posted on nothingbutsharepoint.com.

As a teaser to get you going, here is a quick screenshot of the final product, which we will have after the third post in this series.
The mega menu

The Markup

Hello, folks! Dustin here, and I’m going to dive right in and discuss the markup Heather and I will be using to create a SharePoint-friendly mega menu, and the rationale behind that markup. Yes, that’s right. No SharePoint.

Wait, what about SharePoint?

What about it? SharePoint is not the business requirement here. The business requirement is to create a mega menu. It’s our strong belief that the business requirements inform the technology decisions, not the other way around. Since, in this case, the requirements for the mega menu consist of the HTML and CSS that will be used to present the navigation, that’s what gets created first. Leave SharePoint out of it. SharePoint is a dirty word. For now it is, anyway.

Functional requirements

In simple numbered-list form, so it’s easier to verify that they’re being met:

  1. A series of links to be categorized under a top-level menu item
  2. Links should allow for arbitrary HTML markup for their descriptions
  3. Links should optionally support categorization or grouping
  4. Markup should allow for flexibility in styling
  5. Markup should allow for easy enhancement with client-side scripting

Technical requirements, A.K.A. HTML Markup

We’re both big fans of clean, semantic markup. As a developer with designer skills, I always try to create markup that isn’t only easy to manipulate with client-side scripting (JavaScript) but server-side processing and building as well (XSL). Additionally, the markup that’s created should be easily reusable; thoughtfully designed HTML can be repurposed on multiple sites with little effort.

In this case, the navigation is truly a list of navigation items. Each navigation item will have a “child” list of sub-navigation items. Since it’s a mega menu, it should also have the ability to group sub-navigation items. Perhaps grouping could be used for categorization of the second level of navigation links, or may just be used to render them into columns or blocks. Heck, this structure should be flexible enough that one could create a ribbon-style UI with only CSS.

Top level links

It’s a navigation menu. How about some links? And, hey, why not throw in some HTML5 markup so the menu can hang with the cool kids. A nav element will act as the wrapper around the mega menu, and a simple ul (un-ordered list) will contain the links themselves.

<nav> <!-- Container start -->
    <ul> <!-- Top level links start -->
        <li class="nav-SearchEngines">
            <a href="#">Search Engines</a>
        </li>
        <li class="nav-TimeKillers">
            <a href="#">Time Killers</a>
        </li>
        <li class="nav-OurStuff">
            <a href="#">Our Stuff</a>
        </li>
    </ul> <!-- Top level links end -->
</nav> <!-- Container end -->

Requirements met?

Requirement number one is met already. Top level menu items used to categorize the links.

Requirements number four and five are met (so far) as well. The markup is clean and simple. There are extra hooks for CSS and client-side script that be used to style each top level link in a different way without resorting to index-based selectors. The class name used on the li element is based on the actual text content of the link — something that will be easy to automate later once the custom SharePoint view is built to generate the mega menu.

Second level: Ungrouped links

The second level of the mega menu will allow for links to be a simple list of navigation items, but should also support the ability to group those links into categories or other semantic structures. First, un-grouped links. Structurally, since these links are “children” of the top-level links, the top level li items will contain a child ul element with the second-level links.

Each second-level link can include a div element that will support any markup one would want to use to describe the link.

<!-- Previous markup snipped -->
<li class="nav-SearchEngines">
    <a href="#">Search Engines</a>
    <ul>
        <li class="nav-Google">
            <a href="http://www.google.com/">Google</a>
            <div>
                <p>Just cuz you're used to it.</p>
            </div>
        </li>
        <li class="nav-Bing">
            <a href="http://bing.com">Bing</a>
            <div>
                <p>
                    <span style="text-decoration:underline">B</span>ing
                    <span style="text-decoration:underline">I</span>s
                    <span style="text-decoration:underline">N</span>ot
                    <span style="text-decoration:underline">G</span>oogle
                </p>
            </div>
        </li>
    </ul>
</li>
<-- Additional markup skipped -->

Requirements met?

The div elements contain the description of the links, so requirement number two is met. Requirements four and five are still being met by this added markup; classes and simple markup will make this easy to style and enhance with script.

Second level: Grouped links

Now it gets a little trickier. How should the markup show that links are grouped, while still ensuring that the menu is easily styled with CSS and enhanced with JavaScript?

I thought about this one for a while, and it came to me: The groups of links are, themselves, items in a list. A list of groups. The links in each group are sub-lists beneath each group.

<li class="nav-OurStuff">
    <a href="">Our Stuff</a>
    <ul class="nav-hasgroups"> <!-- Groups list begin -->
        <li class="navgroup-Products"> <!-- Grouped links for "Products" begin -->
            <h1>Products</h1> <!-- Name of group -->
            <ul> <!-- Links for "Products" group begin -->
                <li class="nav-Widgets">
                    <a href="http://google.com/search?q=widgets">Widgets</a>
                    <div>
                        <h2>
                            <img alt="" src="http://placehold.it/75x50.png"/>
                            Widgets and thingamabobs.
                        </h2>
                        <p>Read all about our widgets and thingamabobs.</p>
                    </div>
                </li>
                <li class="nav-Dongles">
                    <a href="http://google.com/search?q=dongles">Dongles</a>
                    <div>
                        <h2>
                            <img alt="" src="http://placehold.it/75x50.png"/>
                            Dongles and doodads.
                        </h2>
                        <p>Read all about our dongles and doodads.</p>
                    </div>
                </li>
            </ul> <!-- Links for "Products" group end -->
        </li> <!-- Group links for "Products" end -->
        <!-- Additional grouped links snipped for brevity -->
    </ul>
</li>

Requirements met?

Requirements one, two, four and five are still met by this code. Number four was the tricky one, but by adding “nav-hasgroups” as a CSS class to the ul containing the groups, styling should be simple. Requirement three specified that the links should optionally support categorization or grouping; the latest example markup meets the grouping requirement.

So far, so good.

The markup is done (and you can download it here: http://spexp.me/megamenusource).

Next up, Heather takes over and walks through the first round of CSS to style the list so it looks like a menu.

The CSS for the Markup

Now that Dustin has created the markup for the mega menu, it needs to look like actual navigation you would see on a web site and not just a simple bulleted list. Using just CSS, the list can be converted into a dynamic navigation bar with drop down menus and hover effects.

There will likely be other bulleted lists in the site’s content, so a parent element should be included in the CSS selectors so only the mega menu is styled and not every list on the page. In this example the parent is “nav” but it could be anything such as “div” or a class or ID assigned to the parent element.

Additionally most of the selectors in this sample use child selectors – it is the greater than symbol (>) between the elements. A child selector is more specific than a descendant selector which would omit the greater than symbol. A child selector dictates that the following element must be a direct child of the preceding element whereas the descendant selector says the child element can be anywhere within the preceding element, even if that means three nested levels down. Since our HTML structure is nested groups of data, the child selectors are needed so we can specifically target a certain level of the lists.

The other thing included with this code is a LOT of comments. Adding comments is vital for CSS maintenance (and sanity) and really helps when you get into more complex CSS such as this.

Convert to a simple horizontal menu bar

  1. Before the content can get styled into a navigation bar, there needs to be less of it shown to the users. The idea is after all for all the content to be within an interactive menu. This first selector is going to target the child lists that contain the content, absolute position it which removes it from the natural content flow, and then move it off the visible screen.
    /* ---------- Menu - Level 2 ---------- */
    nav > ul > li > ul {
        position: absolute;
        left: -9999px; /* Hides the content */
    }
  2. Now that the list is simplified, the bullets need to be removed from the list. Altering the list style will remove the bullets but the spacing still remains so the padding and margin also need to be zeroed out.
    /* ---------- Menu - General ---------- */
    /* Remove default list formatting */
    nav ul {
        list-style: none;
        padding: 0;
        margin: 0;
    }
  3. There are two different ways to convert the unordered list to a horizontal bar. One, float the list items. Or two, change the display for the list items from “block” to “inline” or “inline-block”. This really boils down to personal preference or which one will support the finer details of the design. We have used both. For more in-depth information about this, check out Float vs. Inline-Block. This example will use float. When you float any element, you have to specify a width. It can be auto or a fixed width value.
    /* ---------- Menu - Level 1 ---------- */
    /* List item container */
    nav > ul > li {
        float: left;
        width: auto; /* Required for the float property - Can optionally set a fixed width here */
    }

    Floating an element removes it from the natural flow of the content and places it on top like a stickie note stuck to a piece of paper. The list container (ul) is a block level element. Floating the list items (li) within it creates the horizontal text flow of the menu. Floated elements will stack up next to each other. The floated list items don’t appear at the top of the web page because their positioning is relative to their parent, which in this case is the list container (ul).

  4. This style statement can be expanded to include properties that take this boring text to something that looks more like web site navigation links. The following will add some space around each navigation item, color the background and add a border to the left side of each item so we have a visual separation of navigation links.
    padding: 10px;
    background: #614B59;
    border-right: 1px solid #ADACA2;
  5. The previous style statement is formatting the list item container, but not the text itself. Since this text is wrapped in anchor tags, a new style statement is needed that uses a selector that targets the anchors within the list items. It is always a great practice to write the selector three times, using the pseudo classes that are associated with hyperlinks. Pseudo classes act like modifiers for the selectors. Once the anchors have been targeted, set the font color, family (typeface) and size.
    /* List item text */
    nav > ul > li > a,
    nav > ul > li > a:link,
    nav > ul > li > a:visited {
        color: white;
        font-family: Calibri, Verdana, sans-serif;
        font-size: 16px;
    }
  6. To round out the navigation text, add a hover effect. The hover is created using the hover pseudo class in a new style statement that specifies what should change when you mouse over the text. Typically you see changes in the background treatment. If the change between off and hover state backgrounds is drastically different, the hover text color needs to be modified as well.
    /* List item container hover effect */
    nav > ul > li:hover {
        background: #BFC47D;
    }
    
    /* List item text hover effect */
    nav > ul > li:hover > a {
        color:black;
    }

Here is a screenshot of what has been done so far to our menu:

The mega menu

More cool factor can be added in a little while. This is a good stopping point for the basic navigation bar and it is time to switch to how to show and style the content under each navigation item. Here is a cumulative and organized copy of all of the code created so far:

/* ---------- Menu - General ---------- */
    /* Remove default list formatting */
        nav ul {
            list-style: none;
            padding: 0;
            margin: 0;
        }

/* ---------- Menu - Level 1 ---------- */
    /* List item container */
        nav > ul > li {
            float: left;
            width: auto; /* Required for the float property - Can optionally set a fixed width here */
            padding: 12px;
            background: #614B59;
            border-right: 1px solid #ADACA2;
        }

    /* List item container hover effect */
        nav > ul > li:hover {
            background: #BFC47D;
        }

    /* List item text */
        nav > ul > li > a,
        nav > ul > li > a:link,
        nav > ul > li > a:visited {
            color: white;
            font-family: Calibri, Verdana, sans-serif;
            font-size: 16px;
        }

    /* List item text hover effect */
        nav > ul > li:hover > a {
            color:black;
        }

/* ---------- Menu - Level 2 ---------- */
    /* List container */
        nav > ul > li > ul {
            position: absolute;
            left: -9999px; /* Hides the content */
        }

Show and style the level 2 menu – a.k.a. your drop down

  1. The first selector written out for the CSS moved the child unordered lists (the contents for each navigation item) off the screen so it wasn’t visible to the users. It can be displayed again by changing that placement to put it back on the screen. Since the contents should show when the user hovers over the navigation text, the selector that will show the content again will use the :hover pseudo selector. Duplicate the menu level 2 list container selector created earlier and change the left property value:
    /* Show 2nd level lists when parent item is hovered */
    nav > ul > li:hover > ul {
        left: 0;
        top: auto;
    }

    There is one thing that needs to get tweaked when this style statement is added. The second level lists are being absolute positioned and therefore are no longer in the natural flow of the content. Absolute positioning is set by specifying values for properties like top, bottom, right and left. For example, left: 15px. This would place the element 15 pixels in from the left side. The trick is which left side? Left side of the browser? Not necessarily. The starting position is based on the position of the parent. This is where the tweak comes in. The parent needs to be called out as the starting point for positioning. In this case, it is the list item container for the first level menu. Adding this property will set it as the starting position:

    position: relative;

    There is already a style statement for this selector so the new property can just get folded into it:

    /* List item container (level 1) */
    nav > ul > li {
        float: left;
        width: auto; /* Required for the float property */
        padding: 12px;
        background: #614B59;
        border-right: 1px solid #ADACA2;
        position: relative;
    }
  2. The drop downs are now popping in on hover but look totally blah. It is time to go back to the style statement that controls the initial left positioning and start adding some more styles. If you are using a handy browser add-on/tool/extension that allows for live editing and preview of the CSS, it can be helpful to temporarily hide the left property that moves the list off the screen while you work on the styling. Just remember to focus on how it looks when you hover instead of how it looks when you do not. Move the opening comment characters before the left property as shown below.
    /* List container (level 2) */
    nav > ul > li > ul {
        position: absolute;
        /* left: -9999px; Hides the content */
    
    }

    Start adding properties to create a background and optionally, a set width. The following adds a background color, sets a border on all sides but the top, sets a fixed width so the pop-up content has plenty of space and adds a top margin to move the drop down menu away from the navigation item text.

    background: #E3E2B3;
    border: 1px solid #BFC47D;
    border-top: 0;
    width: 250px;
    margin-top: 10px;
  3. Once the container has been styled focus can turn to the list items within. Just like before, create a selector that targets second nested list items and add properties to control the style. In this example padding is added to move the text off the boundary edges and a top border is added to help create definition between the list items.
    /* List item container (level 2) */
    nav > ul > li > ul > li {
        padding: 10px 8px;
        border-top: 1px solid #614B59;
    }
  4. A hover effect can be added as well to the second list item level.
    /* List item container hover effect */
    nav > ul > li > ul > li:hover { 
        background: white; 
    }

Check out a screenshot of menu updates:

The mega menu

The menu can be considered done, or you can add a little bling. Before the bedazzler is whipped out, here is an updated copy of the cumulative and organized code:

/* ---------- Menu - General ---------- */
    /* Remove default list formatting */
        nav ul {
            list-style: none;
            padding: 0;
            margin: 0;
        }

/* ---------- Menu - Level 1 ---------- */
    /* List item container */
        nav > ul > li {
            float: left;
            width: auto; /* Required for the float property - Can optionally set a fixed width here */
            padding: 12px;
            background: #614B59;
            border-right: 1px solid #ADACA2;
            position: relative;
        }

    /* List item container hover effect */
        nav > ul > li:hover {
            background: #BFC47D;
        }

    /* List item text */
        nav > ul > li > a,
        nav > ul > li > a:link,
        nav > ul > li > a:visited {
            color: white;
            font-family: Calibri, Verdana, sans-serif;
            font-size: 16px;
        }

    /* List item text hover effect */
        nav > ul > li:hover > a {
            color:black;
        }

/* ---------- Menu - Level 2 ---------- */
    /* List container */
        nav > ul > li > ul {
            position: absolute;
            left: -9999px; /* Hides the content */
            background: #E3E2B3;
            border: 1px solid #BFC47D;
            border-top: 0;
            width: 250px;
            margin-top: 10px;
        }

    /* List item container */
        nav > ul > li > ul > li {
            padding: 10px 8px;
            border-top: 1px solid #614B59;
        }

    /* List item container hover effect */
        nav > ul > li > ul > li:hover { 
            background: white; 
        }

    /* Show 2nd level lists when parent item is hovered */
        nav > ul > li:hover > ul {
            left: 0;
            top: auto;
        }

Part Two of the Mega Menu Series

Check out the next post in this series.