Toggle Navigation – No JavaScript

This article is one in a series of posts highlighting the branding, design and development process behind this website.  Read more at Joe Snell – Branding & Front End Development.

Toggle Nav NO JS - CodePen ImageIn this tutorial, I will demonstrate a simplified version of the responsive toggle navigation pattern I am using on my website. I will show how I built this navigation pattern without using JavaSript and how I utilized it within my WordPress Theme. I used Sass, Compass and custom CSS to build my navigation, but I’ve chosen to simplify the CSS and HTML of the demonstration so it will be easier for others to duplicate and re-use in their projects. Below is a live demo via CodePen:

Check out this Pen!

When building a responsive website, one of the many design decisions that must be made is how to handle navigation. There are many of design patterns to choose from. Brad Frost, responsive web ninja, put together a great article regarding Responsive Navigation Patterns and a resource for some of those patterns on This Is Responsive.

When I was putting pen to paper on my redesign, I had always envisioned using a toggle navigation pattern when viewing my site on small screens. I began with a JavaScript approach to the navigation pattern, but eventually switched to use a CSS only approach without using JavaScript and utilized it within my WordPress Theme.

I was inspired to go with a CSS / no JS approach to my toggle navigation after reading This Is An Updated Website by Brad Frost and Build A Smart Mobile Navigation Without Hacks by Aaron Gustafson. Coincidently, since building my navigation pattern, another great resource on the utilization of CSS navigation patterns was written by David Bushnell – Implementing Off-Canvas Navigation for a Responsive Website.  These posts do a tremendous job explaining the benefits and pitfalls of using a CSS only navigation pattern, so I won’t get into that in this tutorial.

To begin the tutorial, set up a basic HTML structure. For those familiar with constructing WordPress themes, build a similar structure with the header.php, index.php and footer.php files. Hopefully, this should be somewhat self-explanatory:


<div id="main" class="main demo">
  <header id="header" class="header-demo">
    <!-- Header  Content Here -->
  </header>
  <section id="content" class="content">
    <!-- Main Content Here -->
  </section>
  <footer id="footer" class="footer">
    <!-- Footer Content Here -->
  </footer>
</div>

The next step is to add the <header> content by inserting a site title, the site navigation (<ul> class=“menu”><ul>), and the ‘menu-link.’ The ‘menu-link’ will be used to toggle open the navigation menu by targeting #menu – the navigation. In WordPress, see the comments below for substituting the <ul> with the ‘primary’ theme menu:


<header id="header" class="header-demo">
  <h3>Site Title</h3>
  <a id="jump-top" href="#menu" class="menu-link ">Menu</a>
  <nav id="main-nav" roll="navigation" class="main-nav">
    <!-- In WordPress, substitute the following <ul> with the ‘primary’ navigation by inserting the following ('primary' refers to the menu slug given in the WordPress theme – dashboard > appearance > menus):
    <?php wp_nav_menu( array( 'theme_location' => 'primary', 'container' => '', 'menu_id' => 'menu' ) ); ?>
    -->
    <ul id="menu" class="menu">
      <li><a href="#">Home</a></li>
      <li><a href="#">Blog</a> </li>
      <li><a href="#">Products</a></li>
      <li><a href="#">About Us</a></li>
      <li><a href="#">Contact</a></li>       
    </ul>
  </nav>
</header>

Now that the base structure is set up, the next step is to add some CSS. I’m working with the assumption that some type of reset or base CSS is being used. Feel free to use my Utility CSS for this demonstration. The CSS below will display the <header> as a bar with a set height. By absolutely positioning the ‘menu-link’ and giving it z-index:1001, it will ensure the ‘menu-link’ is ‘above’ the header and click/touch accessible, while giving the appearance it rests within the <header> Additionally, I’ve also styled our navigation <a> elements:


.header-demo {
  height: 3em;
  background-color: #999999;
}
.header-demo h3 {
  float: right;
  margin: 0;
}
.menu-link {
  color: white;
  text-shadow: none;
  background-color: black;
  padding: 0.75em 0.75em 0;
  height: 3em;
  position: absolute;
  left: 0;
  top: 0;
  z-index: 1001;
}
.menu-link:hover {
  text-decoration: none;
  background-color: #222222;
}
.menu li a {
  color: #999999;
  text-shadow: none;
  text-decoration: none;
  background-color: #222222;
  display: inline-block;
  height: 2em;
  line-height: 1em;
  padding: .4em;
  border-bottom: 1px solid #333333;
  width: 100%;
}
.menu li a:hover {
  color: #aaa;
  background-color: #333333;
}

The page should now be taking shape, although the navigation menu may look a little wonky at this point.

To toggle the navigation, I am using the :target attribute and the id’s I assigned in the HTML structure. I am using body:not(:target) for the base items to prevent style bleed. This comes directly from Aaron Gustafson’s Build A Smart Navigation Without Hacks:

Sure, if a browser supports media queries, it probably supports :target, but just in case, I opted to preface every relevant style rule withbody:not(:target) (which would only be matched if the browser supported target selection).

To place the <ul> correctly, I’ve absolutely positioned it just below the <header> by giving it padding-top:3em; matching the <header>‘s height and stretching it across the screen with left: 0; and right: 0;. Then, I use CSS to hide the <li> elements and, therefore, hide the navigation menu from view – the goal for its non-targeted state:


body:not(:target) #menu {
  margin: 0;
  padding-top: 3em;
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
}
body:not(:target) #menu li {
  width: 100%;
  height: 0;
  line-height: 0;
  overflow: hidden;
}

To make the toggle navigation menu come alive, I next style the corresponding :target li elements to display them vertically when toggled:


body:not(:target) #menu:target li {
  height: 2em;
  line-height: auto;
  overflow: visible;
}

The primary components of the responsive toggle navigation and the corresponding CSS are in place – a toggle menu is in effect. Selecting the ‘menu link’ will now trigger the menu drop-down.

In order to close the menu without having to click on one of the menu links, I’ll need to add a special component. Taken again from Build A Smart Mobile Navigation Without Hacks, I first add a new link .back to the bottom of the navigation <ul>. This new link will be used to ‘close’ the toggle menu. Using WordPress, this link can be manually added by going to dashboard > appearance > menus in WordPress.


<ul id="menu" class="menu">
  <li><a href="#">Home</a></li>
  <li><a href="#">Blog</a></li>
  <li><a href="#">Products</a></li>
  <li><a href="#">About Us</a></li>
  <li><a href="#">Contact</a></li>
  <li class="back"><a href="#header">back</a></li>
</ul>

To get this new link to work correctly, add the following CSS to position it above the content:


body:not(:target) #menu:target .back {
  height: 0;
  line-height: 0;
}
body:not(:target) #menu:target .back a {
  width: 100%;
  background-color: transparent;
  border: none;
  height: auto;
  position: absolute;
  top: -101em;
  bottom: -101em;
  left: 0;
  right: 0;
  text-indent: -999em;
  z-index: -1;
}

The link now works when clicking / touching the content area, but not the header area. To fix this, first add z-index: 1000; to the existing body:not(:target) #menu in the CSS:


body:not(:target) #menu {
  margin: 0;
  padding-top: 3em;
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  z-index: 1000;
}

Next, add a new CSS item, body:not(:target) #menu:target, below body:not(:target) #menu li a:


body:not(:target) #menu:target {
  z-index: 1001;
}

The toggle navigation is fully operational. To improve the UX, I’ve added a transition effect to the toggle. Although I use Sass and Compass, this is the extended CSS transition effect added to the corresponding <li> elements.


body:not(:target) #menu li {
  width: 100%;
  height: 0;
  line-height: 0;
  overflow: hidden;
  -webkit-transition: height 0.25s;
  -moz-transition: height 0.25s;
  -o-transition: height 0.25s;
  transition: height 0.25s;
}

And:


body:not(:target) #menu:target li {
  height: 2em;
  line-height: auto;
  overflow: visible;
  -webkit-transition: height 0.25s;
  -moz-transition: height 0.25s;
  -o-transition: height 0.25s;
  transition: height 0.25s;
}

To make this toggle navigation responsive, I’ve inserted a media query breakpoint from which I’ve removed the toggle menu entirely and replaced it with standard navigation. I’ve also injected some CSS to alter the display properties of the elements:


@media screen and (min-width: 42em) {
  .menu-link {
    display: none;
  }
  body:not(:target) #menu {
    padding-top: 0;
  }
  body:not(:target) #menu li {
    display: inline;
    border: none;
  }
  body:not(:target) #menu li a {
    color: #555555;
    font-weight: bold;
    text-shadow: inherit;
    line-height: 1em;
    padding: .75em;
    height: 2em;
    width: auto;
    border: none;
    background-color: transparent;
  }
  body:not(:target) #menu li a:hover {
    color: black;
  }
  body:not(:target) #menu .back {
    display: none;
  }
}

This is now a fully functional responsive toggle navigation without using JavaScript. Again, below is a live demo via CodePen:

Check out this Pen!

Hopefully, this tutorial has been helpful in demonstrating how to build and deploy a responsive toggle navigation without using JavaScript. If you have suggestions on how to improve the code, found this tutorial helpful or have any other comments, I’d love to hear from you in the comments section below.

I’d like to thank Brad Frost and Aaron Gustafson for their inspiration and resources:

Brad Frost:
This Is An Updated Website
Responsive Navigation Patterns
This Is Responsive – Navigation

Aaron Gustafson:
Build A Smart Mobile Navigation Without Hacks

24 thoughts on “Toggle Navigation – No JavaScript

  1. Pingback: design for the web » Blog Archive » responsive navigation links

  2. Hi Joe,

    I’m trying to use this navigation on a page I’m building. However, I’d like to have the navigation on the side up until I resize things for mobile. On page load the browser scrolls down to the navigation, skipping the header. I can’t seem to see what in your css is causing this. What am I missing?

    Thanks!

  3. Great post thanks! I’ve only got two followup questions. First what is a style bleed? And second do you think the following would be possible ( i tried but i failed so far). Taking your example, in a non clicked state your menu-link button has a background-color of black. Would it be possible now that that color changes in a clicked state e.g. to red and switches back to black when the menu isn’t the target anymore? Best regards Ralf

    • Hello RPK. Thank you for your response!

      Style bleed is when css lacks specificity so the styles may affect elements outside of the specified targeted elements. In this case, we want to make sure these nav styles and :target styles don’t effect (style bleed) anything else on the page.

      Yes! You can have a target state on the menu link. I created a modified demo to show you. Basically, you need to put the menu link within the ul.

      • hi joe!

        cool! I just didn’t knew the word but the meaning behind i am used to. ;) thanks for the explantation!

        ahhhh indeed an easy solution. cool cool cool and one refinement suggestion for your pen. Add a “z-index:-1;” to the “body:not(:target) #menu:target .menu-link” then the button works again for closing the nav. Otherwise it gets unresponsive cuz it resides now over the “.back a” element.

        • Ahh… good call on the z-index! Made the change in the altered demo.

          Glad you found the demo helpful and thanks for your questions and comments!

          Happy coding!

          Joe

          • hi joe, meanwhile i got everything running smoothly regarding that menu pattern thanks to your input and assistance.
            But i guess i discovered an edge case, which might be of interested and in relation to that article and i am just curious if you or one of the other readers has experienced something similar yet.
            In the case you have an one page site where you jump to the different sections by anchors placed in the menu pattern above. Everything works fine. But if you bring in any smooth scrolling plugin or code snippets (i’ve used smooth scroll – https://github.com/kswedberg/jquery-smooth-scroll ) parts of the menu stop working (e.g. the toggle button gets unresponsive or the back functionality). It seems the preventDefault() function which is part of nearly every smooth scroll plugin or snippet is causing that issue. If you comment out the function it works again. Just curious if you also ran into that issue already and might have an idea to bypass that interference. Because just to comment out the preventDefault() seems a bit brute force and basically wrong imho. ;) Best regards Ralf

          • Hey Ralf!

            You can absolutely do both. There are many ways to do it, but I’d recommend using the .not() method in your js.

            Again, I made a ‘very quick’ demonstration. Sorry, I’d have liked spend more time on it. When I get a chance, I’ll try and build a scroll demo with no js. ;-)

            I hope this helps!

  4. Hello Joe Snell!

    I’m someone, who is still an absolute beginner in HTML and CSS. At the moment, I’m working on a small experimental test website, which isn’t online, but it’s supposed to have a toggle menu, when it’s being displayed an mobile devices. This toggle menu, that I plan on doing, is also supposed to have a 2nd level submenu, like the one on http://webdesignerwall.com/demo/mobile-nav/ .
    Is it possible to design a toggle menu in such a way, like the before mentioned demo by webdesignerwall.com, without javascript?
    I’ve also noticed, that I can’t seem to close your toggle menu, whenever I click on Menu. Is that deliberate, or is that an error in the CSS script?
    Other than that, I found your tutorial very informative and insiteful. Keep up the great work you are doing! Thank you.

    Kind regards

    AnonyK

    • Hello Anony,

      Thank you for your comment!

      You can do submenus for sure. The key thing is your HTML output and how you plan on handling that.

      I built a demo for a client that uses my fly-out nav with submenus. You could use that as an example of how to construct the code.

      For other resources, I’d definitely check out the navigation patterns at This Is Responsive by Brad Frost.

      Let me know if you get stuck and I’ll try to offer assistance if I can.

      Thanks again and happy coding!

  5. Very good article. I also try to build a responsive toogle menu and it will help me a little bit more. If I discover something different, I will share it here. Thanks

  6. Hmmm by changing the html5 header to div id=’header-demo’ class=’header-demo’ is making menu ‘hide’ when clicking somewhere in a page. Any idea how to fix this ?

  7. Hi Joe,

    Fantastic article and code thanks, all working brilliantly until you view in good old IE7/8 :o( They display the whole menu as if the toggle has been clicked. Any suggestions how to get it working for them? I’d really appreciate it and I’m sure others would too.

    Thanks

    • Yeah. Unfortunately, that is the best it can do in ie7/8. It still ‘works,’ just not the same. I have a new version that is better xbrowser, but have yet had time to put it on here.

  8. Hi Joe. Great tutorial. I didn’t understand every single line of the CSS but I added each line one at a time to try and understand what’s happening.

    I did encounter one problem, though. I found that the black Menu anchor link extends about 10-11 pixels below the gray header.

    • I found that I had to change the height of .menu-link 2.25em instead of 3em.

  9. Joe,
    I’m also finding that when I use your code from Codepen, everything works fine except that when I hit the Menu button to display the menu items, and I’m using either the Safari or Chrome browsers on my iPhone (iOS 7.1.2), I get a gap between the header and the right edge of the screen. When I tap the Menu button again (or any part of the content area), the dropdown menu collapses as it should, the header slides back to the right, and the gap disappears. This doesn’t happen when I’m using any of my desktop browsers. Was this code tested on any mobile phones? Thanks.

    • Robert, in codepen, I am using a utility reset css file. You can find that by clicking the cog on the top left of the css window. I looked at the fiddle you sent me and if you add these classes your primary issue will be fixed:

      body {
      margin: 0;
      }
      ul {
      list-style: none;
      padding: 0;
      }

Leave a Reply