HTML & CSS
Article
By Kalpesh Singh

Building Mega Menus with Flexbox

By Kalpesh Singh

Mega Menus with Flexbox

As you are probably aware, Flexbox has recently gained momentum with increasing browser support. It allows developers to build complex user interfaces without dealing with unwanted CSS and JavaScript hacks.

Flexbox uses a linear layout model, which allows us to lay our content horizontally or vertically without involving calculations for spacing. The flex layout reacts responsively to the elements within the container, thus requiring fewer media queries.

In this article I will make use of this layout model to build a mega navigation menu and in the process you’ll see how simple it is to build and extend user interface components with flexbox.

I won’t discuss individual Flexbox properties in detail here, but instead the focus will be on the practical use of the features. For a basic introduction to Flexbox, please refer the following resources:

What Are We Building?

To get an idea of what I’ll be showing you how to build, take a look at the full-screen CodePen.

This tutorial is divided into three parts:

  1. Building the navigation bar: Using flexbox to build a simple navigation bar for our imaginary e-commerce platform
  2. Building a single drop-down section
  3. Limiting a single drop-down section to three columns

Building the Navigation Bar

The markup for the navigation bar is simple. I will handle everything with two classes (navbar and menu) for the sake of this demo. The CSS here will exclude any styles unrelated to the tutorial.

<nav class="navbar">
  <ul class="menu">
    <li>
      <a href="#">
        Electronics
        <!-- FontAwesome icon -->
        <i class="fa fa-angle-down"></i>
      </a> 
    </li>

    <!-- ... More nav items here... -->

  <ul>
</nav>

The navbar class is responsible for centering our navigation bar in the available space, but I’m going to focus on the menu class where I will use flexbox.

I want my navigation items to be laid out horizontally. Also, I want each item to be spaced equally and shrink as needed if there is not enough space.

First, I need to establish a flex formatting context on the .menu element, which I’ll do with display: flex. Now, all the direct children of the .menu element (which is the flex container) will be flex items.

Next, I want the menu items to be equal in width. I’ve added flex: 1 to make them grow uniformly with equal width. Here is the code:

.navbar .menu {
  display: flex;
  position: relative;
}

.navbar .menu li {
  flex: 1;
  display: flex;
  text-align: center;
}

.navbar .menu a {
  flex: 1;
  justify-content: center;
  color: #ffffff;
  padding: 20px;
}

Looking at the code, you might be wondering why I repeated display: flex for all the flex items (.navbar .menu li).

In the demo, when you hover over a menu item, its background color changes. If I don’t set the display property of the flex items to flex, then only the li elements will have equal width and not the content inside them (i.e. some highlighted part will be clickable and others will not).

To make the content expand to the full width of its parent, I made the flex items themselves into flex containers. With this in place, I can then make each nested a element grow as wide as its parent (using flex: 1), thus making the full highlighted area clickable.

Alternatively, I could have also achieved this without making li elements flex containers, but I’d have to use three additional properties (display: inline-block, width: 100%, box-sizing: border-box), which I would prefer to avoid.

This demo shows what is done so far.

With a mere five CSS Flexbox properties, the navigation bar is ready. As you can see, this is a clean solution.

In the next section, I’ll show you how to build a single section of the mega navigation.

Building a Single Drop-down Section

Below is the markup I’m going to use to build a single drop-down section, which will extend to multiple sections. The container__list item will be duplicated to create the other sections.

<ul class="container">
  <!-- single column -->
  <div class="container__list">
    <!-- menu item -->
    <div class="container__listItem">
      <div>Televisions</div>
    </div>

    <!-- ... other menu items here -->

  </div>
</ul>

The container is the flex container and each direct child (i.e. container__list) is a flex item. Each container__list has multiple navigation items, each wrapped by container__listItem. I’ve wrapped the content in a div, which I’ll come back to later.

Here is the CSS:

.container {
  /* initially hidden; display:flex on hover */
  display: none;
  position: absolute;
  top: 56px;
  left: 0;
  right: 0;
  background-color: #ffffff;
  padding: 20px;
  text-align: left;
}

.container__list {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
}

.container__listItem {
  flex: 0 0 25%;
  padding: 10px 30px;
}

.container__listItem > div {
  color: #DB6356;
}

Notice that I’ve used the flex-wrap property on container__list but I didn’t use this for the navigation bar itself. As mentioned earlier, I don’t want the navigation bar items to wrap to the next line when there is a shortage of space. Instead, they shrink equally when the available horizontal space is reduced.

For the container__list items, however, the requirement is the other way around. I want my list item to use 25% of the space, thus accommodating a maximum of four items per row, which I can accomplish using flex-wrap: wrap.

I’ve also set flex-grow to 0. This is useful because it prevents the items that are less than four from being equally spaced. By setting it to 0 I’m forcing the items to stick with 25% of the space.

Now what about the content wrapped in div?. I wanted to cover a case where you may want to prevent content overflowing. This is pretty straightforward when your content is directly inside the flex child (i.e. container__listItem). I can use following code to replace clipped text with an ellipsis (“…”).

.example {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

But in this case I’ve put the content inside a div, which is wrapped by a container__listItem. Thus, the above solution won’t work. The article Flexbox and Truncated Text offers a solution. In the code below, the lines below the “updated” comments in each declaration block are the ones that deal with this problem:

.container__list {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  /* updated */
  min-width: 0;
}

.container__listItem {
  flex: 0 0 25%;
  padding: 10px 30px;
  /* updated */
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.container__listItem > div {
  color: #DB6356;
  /* updated */
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
--ADVERTISEMENT--

Limiting a Single Drop-down Section to Three Columns

At this stage, the bulk of the work is now done. For this remaining section, I’ll add another drop-down section that I’ll force to be limited to three columns of items.

As mentioned, I’ll replicate container__list twice and will use it in a new drop-down section called “Appliances”. This is done for demo purposes. In a real world example, you might have a generated list via JavaScript or using a back-end language.

I’ll add a class of has-multi to tweak the user interface. Using this class I need to override a few properties.

.container.has-multi .container__list {
  flex-basis: 33.333%;
}

.container.has-multi .container__list:not(:last-child) {
  border-right: solid 1px #f3f3f3;
  margin-right: 20px;
}

.container.has-multi .container__listItem {
  flex-basis: 100%;
}

Here I’ve set flex-basis to 33.333% as I want to display three sections in the container. I’ve changed only flex-basis, but the other 2 properties flex-grow and flex-shrink are already inherited from the single section code.
This gives me flexibility when I have a container__list less than three. In the case of only two lists, the container__list items will grow and distribute space among themselves. That is, each will hold 50% of the total width.

Note that .container__listItem is set to flex-basis: 100%, which ensures a single column in the container__list. I could have used 50% to allow two columns in each section.

A Few Notes on Mega Menu Usability

I’ve chosen mega menus as my example without much thought on its usability. Before using such a feature, I’ve provided a few resources that discuss the pros and cons.

In my opinion, mega menu navigation is useful for displaying all options and this style of navigation can be used effectively on e-commerce websites. I like Intel’s navigation.

Final Words

One thing to note is that the final mega menus from this tutorial aren’t fully responsive. The main menu bar will appear on smaller screens but the mega menus will not be available, with only the top-level links functional. For the purposes of this tutorial, this should suffice. Feel free to fork the demo and improve on this if you like.

A mega drop-down menu navigation system is a decent way to demonstrate the power and simplicity of Flexbox, which I hope I have conveyed in this tutorial. As you can see, flexbox is useful for more than just centering content.

If you’ve used a different flexbox-based technique to build a mega menu system, feel free to tell us about it in the comments.

Login or Create Account to Comment
Login Create Account
Recommended
Sponsors
Get the most important and interesting stories in tech. Straight to your inbox, daily.