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!

Do you want to include this resource in a product offered for sale? Learn more about our Extended License

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