CSS-only Responsive Dropdown Navigation Menu

Creating a simple, responsive CSS-only Dropdown Navigation Menu using the mobile-first approach of Responsive Web Design.

CSS-only Responsive Dropdown Navigation Menu
See the Demo

Keeping dropdown navigation menus CSS-only is a tricky job. It’s true that such menus are not touch-friendly in nature, but still, they are handy when you want them without JavaScript.

In this article, I’m going to share how I keep my dropdown menus CSS-only, which I also may extend further to bring about a touch-powered menu using CSS-hacks or JavaScript.

Before going further, I’d recommend you to go through Simple CSS Navigation Menu to see the basic boilerplate of what we are going to use here.

Demo the CSS-only Dropdown Menu

The Idea

As I told above, this one is extended from our simple CSS horiztonal menu. The menu is adjusted to include the sub-menus which also appear fine on smaller screen sizes. We’ll be playing with CSS position property to include and place the sub-menus.

Sub-menu logic

First off, we’ll be setting up the base for our menu. Below go the settings for the navigation wrapper (.nav), the menu link-list (.menu), and the children. Note that the CSS below is based on the mobile-first approach of responsive web design.

Our sub-menus should break out of the flow on bigger screens, which is well taken care by the position CSS property. If we set the position of parent menu-item to relative, then it will become easier for us to make sub-menus behave accordingly in breaking the flow.

SCSS

Study the following SCSS, and see how we aligned the nav-wrapper, menu, and menu-items for the devices with smaller screens.

Basic Alignment

The display menu-items and hyperlinks can be set to block-level to make them appear like solid menu-item with a size. The size can be maintained by providing some padding to the hyperlinks, and the separations between the items can be made using the border.

Fade effect

We all know setting display to none normally, and making it block-level again on hover, active, and focus states will do the job; but this way we won’t be able to get that slick fade-in, fade-out effect.

For the fading effect, we’ll be hiding the sub-menu by setting the visibility to hidden, and opacity and height to 0. Next we can add a transition handler to the .sub-menu for these three properties, and will be adjusting them again on hover, active, and focus states.

.nav {
  background-color: #111;
}

.menu {
  // Giving the hyperlinks some padding for better size and contrast
  a {
    padding: .75em 1.25em;
  }
  
  .menu-item {
    border-width: 0 0 1px; /* Adding seperation between menu-items */
    border-color: #292929;
    &:last-child {
      border-width: 0; /* Removing separation from the last menu-item */
    }

    // Positioning the menu-item to control the flow-breaking by sub-menus
    &.has-children {
      position: relative; 
    }

    // Fade-effect on hover, active, focus states
    &:hover,
    &:active,
    &:focus {
      > .sub-menu {
        opacity: 1;
        visibility: visible;
        height: auto;
      }
    }
  }

  // Sub-menu settings
  .sub-menu {
    border-width: 1px 0 0;
    border-color: #d5d5d5;
    background-color: #eee;
    width: 100%;
    // Transition control to make it look fading
    transition: opacity .25s, visibility .25s, height .25s ease-in-out;
    // Hiding the sub-menu
    opacity: 0;
    visibility: hidden;
    height: 0;

    .menu-item {
      border-color: #d5d5d5;
      color: rgba(black, 0.75);
      a {
        color: rgba(black, .75);
        &:hover,
        &:active,
        &:focus {
          background-color: #d5d5d5;
        }
      }
    }
  }

  // Making menu-items and hyperlinks adopt all the available horizontal space
  .menu-item,
  a {
    display: block;
  }
  
  > .menu-item {
    a {
      color: rgba(white, .75);
      &:hover,
      &:active,
      &:focus {
        background-color: #292929;
      }
    }
  }
  
  .menu-item,
  .sub-menu {
    border-style: solid;
  }
}

Breakpoints

Now comes the crucial part, where we need to reconstruct our menu on bigger screens by adding a media query. Setting the display to flex for our menu link-list will make the menu-items aligned horizontally on bigger screen sizes.

We have also made our sub-menus break out of the flow to give them a look of dropping-down menus. First level sub-menus are dropping towards the bottom, and rest of the others are falling on the extreme sides of their parent sub-menus.

@media only screen and (min-width: 1024px) {
  .menu {

    // Making the first-level menu-items horizontally aligned
    display: flex;
    flex-flow: row wrap;

    // Tweaking the separation
    > li {
      border-width: 0 1px 0 0;
    }
    
    // Making sub-menu to break the flow
    .sub-menu {
      position: absolute;
      z-index: 5000;
      min-width: 200px;
      border-width: 0;
      
      .sub-menu {
        top: 0;
        left: 100%;
      }

      li {
        &:last-child {
          border-width: 0;
        }
      }
    }
  }
}

We may pimp up our menu more further by adding more transitions, colors, etc. depending on the theme, and the need for look and feel.

As you have already seen in the demonstrations, I’ve added a dropdown menu-icon too for the sub-menus’ parents, which transforms on hovering over the menu items. I’ll leave it up to you as an exercise to figure out how it is done.

Demo the CSS-only Dropdown Menu

That’s all about it. I hope you found this one useful. Will be doing more stuff like this in future posts, stay tuned, and don’t forget to share your thoughts. Thanks for the read :)