One of the most challenging part when you’re working on a web projects with lots of content is to make it easy for a user to navigate through this content. One example we can all think of is Amazon: infinite categories, each one with its own sub-categories… that’s why they currently provide an easy-to-access navigation, in the form of a mega-dropdown element on the top-left corner of the page.
We’ve been working on a similar concept, a responsive mega dropdown component with sub-categories. Here is a quick animation we put together to show you our mobile vs desktop approach:
As you can notice from the demo, our dropdown is activated with a tap/click. We could have achieved the same result using a :hover state instead, no need for js (we do provide a :hover fallback in case javascript is disabled). However these decisions should be based on what we think is the best user experience. Users generally expect to click to access new content, while :hover effects mostly detect a “potential action”. This is why we preferred click over :hover state. In case you disagree, switching from one approach to the other is a piece of cake ;)
Icons: Nucleo Library
Creating the structure
The HTML is structured in 2 main elements: the <header>
, containing the dropdown (.cd-dropdown-wrapper
), and the <main>
for all the main content.
The .cd-dropdown-wrapper
contains a .cd-dropdown-trigger
, to trigger the dropdown, and a .cd-dropdown
, which is composed of nested unordered lists.
<header>
<div class="cd-dropdown-wrapper">
<a class="cd-dropdown-trigger" href="#0">Dropdown</a>
<nav class="cd-dropdown">
<h2>Title</h2>
<a href="#0" class="cd-close">Close</a>
<ul class="cd-dropdown-content">
<li>
<form class="cd-search">
<input type="search" placeholder="Search...">
</form>
</li>
<li class="has-children">
<a href="#0">Clothing</a>
<ul class="cd-secondary-dropdown is-hidden">
<li class="go-back"><a href="#0">Menu</a></li>
<li class="see-all"><a href="#0">All Clothing</a></li>
<li class="has-children">
<a href="#0">Accessories</a>
<ul class="is-hidden">
<li class="go-back"><a href="#0">Clothing</a></li>
<li class="see-all"><a href="#0">All Accessories</a></li>
<li class="has-children">
<a href="#0">Beanies</a>
<ul class="is-hidden">
<li class="go-back"><a href="#0">Accessories</a></li>
<li class="see-all"><a href="#0">All Benies</a></li>
<li><a href="#0">Caps & Hats</a></li>
<!-- other list items here -->
</ul>
</li>
<li class="has-children">
<a href="#0">Caps & Hats</a>
<ul class="is-hidden">
<li class="go-back"><a href="#0">Accessories</a></li>
<li class="see-all"><a href="#0">All Caps & Hats</a></li>
<li><a href="#0">Beanies</a></li>
<!-- other list items here -->
</ul>
</li>
<li><a href="#0">Glasses</a></li>
<!-- other list items here -->
</ul>
</li>
<li class="has-children">
<!-- other list items here -->
</li>
<li class="has-children">
<!-- other list items here -->
</li>
<li class="has-children">
<!-- other list items here -->
</li>
</ul> <!-- .cd-secondary-dropdown -->
</li> <!-- .has-children -->
<li class="has-children">
<!-- other list items here -->
</li> <!-- .has-children -->
<li class="has-children">
<!-- other list items here -->
</li> <!-- .has-children -->
<li class="cd-divider">Divider</li>
<li><a href="#0">Page 1</a></li>
<!-- other list items here -->
</ul> <!-- .cd-dropdown-content -->
</nav> <!-- .cd-dropdown -->
</div> <!-- .cd-dropdown-wrapper -->
</header>
<main class="cd-main-content">
<!-- your content here -->
</main>
Adding style
For mobile devices, the basic idea was to let the user focus totally on the dropdown content, once it has been activated.
This is why we assigned a fixed position to the dropdown, and set its width and height to 100%. By default, it is hidden right above the viewport (translateY(-100%)). When the user clicks the trigger element, the .dropdown-is-active
class is added to the dropdown which is translated back into the viewport.
.cd-dropdown {
position: fixed;
z-index: 1;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: translateY(-100%);
transition: transform 0.5s;
}
.cd-dropdown.dropdown-is-active {
transform: translateY(0);
}
When user selects a new sublevel in the dropdown, the visible items are translated to the left outside the viewport (using the .move-out
class), while the new items slide back into the viewport (removing the class .is-hidden
class from their <ul>
parent element).
.cd-dropdown-content.is-hidden, .cd-dropdown-content ul.is-hidden {
/* push the secondary dropdown items to the right */
transform: translateX(100%);
}
.cd-dropdown-content.move-out > li > a, .cd-dropdown-content ul.move-out > li > a {
/* push the dropdown items to the left when secondary dropdown slides in */
transform: translateX(-100%);
}
On bigger devices (viewport width bigger than 1024px), instead, there's enough space to place content side by side, with no need to replace the visible content.
@media only screen and (min-width: 1024px) {
.cd-dropdown {
position: absolute;
top: 100%;
/* reset style*/
height: auto;
width: auto;
opacity: 0;
visibility: hidden;
transform: translateY(30px);
transition: opacity 0.3s 0s, visibility 0s 0.3s, transform 0.3s 0s;
}
.cd-dropdown.dropdown-is-active {
visibility: visible;
opacity: 1;
transform: translateY(0);
transition: opacity 0.3s 0s, visibility 0.3s 0s, transform 0.3s 0s;
}
.cd-dropdown-content {
/* reset mobile style */
position: static;
height: auto;
width: 280px;
}
.cd-dropdown-content .cd-secondary-dropdown, .cd-dropdown-content .cd-dropdown-gallery, .cd-dropdown-content .cd-dropdown-icons {
transform: translateX(0);
left: 100%;
height: auto;
}
.cd-dropdown-content .cd-secondary-dropdown.is-hidden, .cd-dropdown-content .cd-dropdown-gallery.is-hidden, .cd-dropdown-content .cd-dropdown-icons.is-hidden {
/* reset mobile style */
transform: translateX(0);
}
.cd-dropdown-content > .has-children > ul {
visibility: hidden;
}
.cd-dropdown-content > .has-children:hover > ul {
/* when hover over .cd-dropdown-content items - show subnavigation */
visibility: visible;
}
.cd-dropdown-content > .has-children:hover > .cd-secondary-dropdown > li > ul {
/* if .cd-secondary-dropdown is visible - show also subnavigation */
visibility: visible;
}
}
Note: if you want the dropdown to open on the left, add the .open-to-left
class to the .cd-dropdown-wrapper
element.
Events handling
We didn’t do a lot in jQuery, apart from listening to the click event on specific elements (e.g. .cd-dropdown-trigger
, .go-back
) and adding/removing classes accordingly.
Note: we integrated the jQuery-menu-aim plugin, a plugin that differentiates between users trying hover over a dropdown item and user trying to navigate into a submenu's contents.