Secondary Fixed Navigation

Secondary Fixed Navigation

A secondary navigation intended for users who want a quick overview of the page content, and be able to easily move from one section of the page to the other.

Nucleo icons

Sponsored by Nucleo, a free application to collect, customize and export all your icons as icon font and SVG symbols. Made by the CodyHouse folks!

All the resources available on CodyHouse are released under the BSD-3-Clause license. You can support our project with a Paypal donation 🙌

A fixed navigation with smooth, jQuery powered scroll. Nothing fancy here, yet a handy snippet for creating a secondary menu to quickly surf through the page content. We see this effect a lot these days, a good example I can think of is Disqus For Websites. A nice touch is to animate the logo and the main call-to-action button to slide in once the navigation becomes fixed.

Icons: Nucleo

Creating the structure

We created a section#cd-intro to wrap our intro image, tagline and call-to-action button.
The secondary navigation is an unordered list inserted in the .cd-secondary-nav element. All the remaining content has been placed in the .cd-main-content element.

<section id="cd-intro">
   <div id="cd-intro-tagline">
      <h1><!-- your tagline here --></h1>
      <a href="#0" class="cd-btn"><!-- your action button text here --></a>
   </div> 
</section>

<div class="cd-secondary-nav">
   <a href="#0" class="cd-secondary-nav-trigger">Menu<span></span></a> <!-- button visible on small devices -->
   <nav>
      <ul>
         <li>
            <a href="#cd-placeholder-1">
               <b>Services</b>
               <span></span><!-- icon -->
            </a>
         </li>
         <!-- other items here -->
      </ul>
   </nav>
</div> <!-- .cd-secondary-nav -->
<main class="cd-main-content">
   <section id="cd-placeholder-1" class="cd-section cd-container">
      <!-- your section content here-->
   </section> <!-- #cd-placeholder-1 -->

   <section id="cd-placeholder-2" class="cd-section cd-container">
      <!-- your section content here-->
   </section> <!-- #cd-placeholder-2 -->

   <!-- other sections here -->
</main> <!-- .cd-main-content -->

Adding style

Since we coded this resource starting from mobile, we assigned a position: fixed to the unordered list inside the .cd-secondary-nav, and placed it at the bottom-right of the viewport. When user taps the .cd-secondary-nav-trigger, we assign the class .is-visible to the unordered list, changing its CSS3 Scale value from 0 to 1.
When the viewport is larger that 1170px, we hide the .cd-secondary-nav-trigger and change the position of the unordered list from fixed to static, so that it is visible inside the .cd-secondary-nav, right after the section#cd-intro.

.cd-secondary-nav ul {
  position: fixed;
  right: 5%;
  bottom: 20px;
  visibility: hidden;
  transform: scale(0);
  transform-origin: 100% 100%;
  transition: transform 0.3s, visibility 0s 0.3s;
}
.cd-secondary-nav ul.is-visible {
  visibility: visible;
  transform: scale(1);
  transition: transform 0.3s, visibility 0s 0s;
}

@media only screen and (min-width: 1170px) {
  .cd-secondary-nav ul {
    /* reset navigation values */
    position: static;
    width: auto;
    max-width: 100%;
    visibility: visible;
    transform: scale(1);
  }
}

.cd-secondary-nav-trigger {
  position: fixed;
  bottom: 20px;
  right: 5%;
  width: 44px;
  height: 44px;
}
@media only screen and (min-width: 1170px) {
  .cd-secondary-nav-trigger {
    display: none;
  }
}

When the user scrolls more than the section#cd-intro height, we assign the .is-fixed class to the .cd-secondary-nav, changing its position from relative to fixed and reducing its height, and then we add the .animate-children class to animate its children. We couldn't use a single class due to a Firefox bug (CSS transition animation fails when parent element changes position attribute). More info about this in the Events handling section below.

@media only screen and (min-width: 1170px) {
  .cd-secondary-nav.is-fixed {
    position: fixed;
    left: 0;
    top: 0;
    height: 70px;
    width: 100%;
  }
  .cd-secondary-nav li a {
    padding: 58px 40px 0 40px;
    transition: padding 0.2s;
  }
  .cd-secondary-nav li a span {
    transition: opacity 0.2s;
  }
  .cd-secondary-nav.animate-children li a {
    padding: 26px 30px 0 30px;
  }
  .cd-secondary-nav.animate-children li a span {
    opacity: 0;
  }
}

We also wanted to show the logo and the call-to-action button when the secondary navigation is fixed. To do so we defined two classes: .is-hidden and .slide-in (the first is assigned when user scrolls more than the #cd-intro-tagline bottom, the second more that .cd-secondary-nav top).

@media only screen and (min-width: 1170px) {
  #cd-logo.is-hidden {
    /* assign a position fixed and move outside the viewport (on the left) */
    opacity: 0;
    position: fixed;
    left: -20%;
    transition: left 0.3s, opacity 0.3s;
  }
  #cd-logo.is-hidden.slide-in {
    /* slide in when the secondary navigation gets fixed */
    left: 5%;
    opacity: 1;
  }

  .cd-btn.is-hidden {
    /* assign a position fixed and move outside the viewport (on the right) */
    opacity: 0;
    position: fixed;
    right: -20%;
    transition: right 0.3s, opacity 0.3s;
  }
  .cd-btn.is-hidden.slide-in {
    /* slide in when the secondary nav gets fixed */
    right: 5%;
    opacity: 1;
  }
}

Events Handling

When user scrolls more than the secondary navigation offset top, we assign it the .is-fixed class to change its position value; we add the .animate-children class with a 50ms delay (due to a Firefox bug) in order to animate its children. Therefore the position value change won't affect the transition, since the they don't happen at the same time.

var secondaryNav = $('.cd-secondary-nav'),
    secondaryNavTopPosition = secondaryNav.offset().top;

$(window).on('scroll', function(){
   if($(window).scrollTop() > secondaryNavTopPosition ) {
      secondaryNav.addClass('is-fixed');	
      setTimeout(function() {
         secondaryNav.addClass('animate-children');
         $('#cd-logo').addClass('slide-in');
         $('.cd-btn').addClass('slide-in');
      }, 50);
   } else {
      secondaryNav.removeClass('is-fixed');
      setTimeout(function() {
         secondaryNav.removeClass('animate-children');
         $('#cd-logo').removeClass('slide-in');
         $('.cd-btn').removeClass('slide-in');
      }, 50);
   }
});

Join our newsletter

Get our monthly recap with the latest CodyHouse news