Accessible Off-canvas Push Menu with jQuery and CSS3

Accessible Off-canvas Push Menu with jQuery and CSS3
File Size: 4.05 KB
Views Total:
Last Update:
Publish Date:
Official Website: Go to website
License: MIT
   

Just another jQuery & CSS3 based off-canvas navigation (push menu) that uses JavaScript to dynamically adds WAI-ARIA roles to make your site navigation more accessible.

How to use it:

1. Create a off-canvas navigation with a toggle button and wrap them together with the main content into a wrapper like this:

<div class="site-wrapper">
  <div class="main">
    <div class="wrap"> 

      <!-- Hamburger menu toggler -->
      <a href="#navigation" title="navigation menu" aria-label="navigation menu"> 
        Navigation Menu 
        <span class="bar bar-1"></span> 
        <span class="bar bar-2"></span> 
        <span class="bar bar-3"></span>
      </a>

      <!-- Main content -->
      ...
    </div>
  </div>

  <!-- Off-canvas navigation -->
  <nav id="navigation" role="navigation">
    <ul>
      <li class="active"><a href="#">Home</a></li>
      <li><a href="#">Works</a></li>
      <li><a href="#">Contact</a></li>
      <li><a href="#">About</a></li>
      <li><a href="#">Blog</a></li>
    </ul>
  </nav>
</div>

2. The basic CSS styles.

body { overflow-x: hidden; }

*:focus { outline: 1px solid #03A9F4; }

.main {
  position: relative;
  padding: 0 20px;
  margin-left: 0;
  -webkit-transition: all 0.3s cubic-bezier(1, 0.1, 0, 0.9);
  transition: all 0.3s cubic-bezier(1, 0.1, 0, 0.9);
  -webkit-transform: translateZ(0) translateX(0);
  transform: translateZ(0) translateX(0);
}

.main > .wrap {
  padding: 80px 0;
  max-width: 1200px;
  margin: 0 auto;
}

[data-nav-visible="true"] .main {
  -webkit-transform: translateZ(0) translateX(300px);
  transform: translateZ(0) translateX(300px);
}

3. Style the hamburger toggle button.

a[href="#navigation"] {
  display: inline-block;
  position: absolute;
  top: 20px;
  left: 20px;
  text-indent: -9999px;
  background: #03A9F4;
  height: 60px;
  width: 60px;
  border-radius: 100%;
  box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.5);
  -webkit-transition: all 0.3s cubic-bezier(1, 0.1, 0, 0.9);
  transition: all 0.3s cubic-bezier(1, 0.1, 0, 0.9);
  -webkit-transform: translateZ(0) scale(1);
  transform: translateZ(0) scale(1);
}

a[href="#navigation"]:hover { opacity: 0.9; }

[data-nav-visible="true"] a[href="#navigation"]:before {
  -webkit-transform: translateZ(0) scale(1);
  transform: translateZ(0) scale(1);
}

[data-nav-visible="true"] a[href="#navigation"] .bar { margin-top: -3px !important; }

[data-nav-visible="true"] a[href="#navigation"] .bar.bar-1 {
  -webkit-transform: translateZ(0) rotate(45deg) translateY(0);
  transform: translateZ(0) rotate(45deg) translateY(0);
}

[data-nav-visible="true"] a[href="#navigation"] .bar.bar-2 { opacity: 0; }

[data-nav-visible="true"] a[href="#navigation"] .bar.bar-3 {
  -webkit-transform: translateZ(0) rotate(-45deg) translateY(0);
  transform: translateZ(0) rotate(-45deg) translateY(0);
}

a[href="#navigation"]:before {
  content: '';
  position: absolute;
  display: block;
  height: 100%;
  width: 100%;
  border-radius: 100%;
  -webkit-transform: translateZ(0) scale(0);
  transform: translateZ(0) scale(0);
  background: #FF5722;
  -webkit-transition: all 1s cubic-bezier(1, -0.25, 0, 1.25);
  transition: all 1s cubic-bezier(1, -0.25, 0, 1.25);
}

a[href="#navigation"] .bar {
  display: block;
  position: absolute;
  background: white;
  height: 4px;
  width: 26px;
  top: 50%;
  left: 50%;
  margin-left: -13px;
  margin-top: -2px;
  border-radius: 2px;
  opacity: 1;
  -webkit-transform: translateZ(0) rotate(0deg);
  transform: translateZ(0) rotate(0deg);
  -webkit-transition: all 0.7s cubic-bezier(1, 0.1, 0, 0.9);
  transition: all 0.7s cubic-bezier(1, 0.1, 0, 0.9);
}

a[href="#navigation"] .bar.bar-1 {
  -webkit-transform: translateZ(0) rotate(0deg) translateY(-8px);
  transform: translateZ(0) rotate(0deg) translateY(-8px);
}

a[href="#navigation"] .bar.bar-3 {
  -webkit-transform: translateZ(0) rotate(0deg) translateY(8px);
  transform: translateZ(0) rotate(0deg) translateY(8px);
}

4. Style the off-canvas navigation.

nav[role="navigation"] {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  padding: 0;
  overflow: hidden;
  background: #111;
  width: 300px;
  box-sizing: border-box;
  -webkit-transition: all 0.3s cubic-bezier(1, 0.1, 0, 0.9);
  transition: all 0.3s cubic-bezier(1, 0.1, 0, 0.9);
  -webkit-transform: translateZ(0) translateX(-100%);
  transform: translateZ(0) translateX(-100%);
}

[data-nav-visible="true"] nav[role="navigation"] {
  -webkit-transform: translateZ(0) translateX(0);
  transform: translateZ(0) translateX(0);
}

nav[role="navigation"] > ul {
  margin: 0;
  padding: 40px 0;
  list-style: none;
  -webkit-transition: all 0.2s ease 0.3s;
  transition: all 0.2s ease 0.3s;
  -webkit-transform: translateZ(0) translateX(100%) scale(0.6);
  transform: translateZ(0) translateX(100%) scale(0.6);
}

[data-nav-visible="true"] nav[role="navigation"] > ul {
  -webkit-transition: all 0.2s ease 0.1s;
  transition: all 0.2s ease 0.1s;
  -webkit-transform: translateZ(0) translateX(0) scale(1);
  transform: translateZ(0) translateX(0) scale(1);
}

nav[role="navigation"] > ul li.active a {
  border-left: 2px solid #03A9F4;
  background: #222;
}

nav[role="navigation"] > ul a {
  display: block;
  padding: 15px 20px;
  color: white;
  font-size: 18px;
  text-decoration: none;
  border-left: 2px solid transparent;
}

nav[role="navigation"] > ul a:hover { background: #03A9F4; }

5. Include the latest version of jQuery library from a CDN.

<script src="//code.jquery.com/jquery-2.1.4.min.js"></script> 

6. The core JavaScript to enable the accessible off-canvas menu.

var Nav = function($) {
  
  return {
    
    init: function() {
      this.cacheDom();
      this.setupAria();
      this.bindEvents();
    },
    
    cacheDom: function() {
      this.$site = $('.site-wrapper');
      this.$navBtn = this.$site.find('[href="#navigation"]');
      this.$navBtnExpanded = this.$site.find('[aria-expanded]');
      this.$nav = $('#navigation');
      this.$navFirstLink = this.$nav.find('li:first-child a');
      this.$navLastLink = this.$nav.find('li:last-child a');
      this.$content = this.$site.find('.content');
    },
    
    bindEvents: function() {
      this.$navBtn.on('click', this.toggleMenu.bind(this));
      this.$navBtnExpanded.on('keydown', this.setFocus.bind(this));
      this.$navFirstLink.on('keydown', this.returnFocusFirst.bind(this));
      this.$navLastLink.on('keydown', this.returnFocusLast.bind(this));
    },
    
    setupAria: function() {
      this.$navBtn.attr({
        'role': 'button',
        'aria-controls': 'navigation',
        'aria-expanded': 'false'
      });
      
      this.$site.attr({
        'data-nav-visible': 'false'
      });
    },
    
    toggleMenu: function() {
      var self = $(event.currentTarget);
      event.preventDefault();    
      self.attr('aria-expanded') === 'true' ? this.closeMenu() : this.openMenu();
    },
    
    openMenu: function() {
      this.$site.attr({
        'data-nav-visible': 'true'
      });
      this.$navBtn.attr({
        'aria-expanded': 'true'
      });
    },
    
    closeMenu: function() {
      this.$site.attr({
        'data-nav-visible': 'false'
      });
      this.$navBtn.attr({
        'aria-expanded': 'false'
      });
    },
    
    returnFocusFirst: function() {
      if (event.keyCode === 9) {
        if (event.shiftKey) {
          event.preventDefault();
          this.$navBtn.focus();
        }
      }
    },
    
    returnFocusLast: function() {
      if (event.keyCode === 9) {
        if (!event.shiftKey) {
          event.preventDefault();
          this.$navBtn.focus();
        }
      }
    },
    
    setFocus: function() {
      var self = $(event.target);
      if (event.keyCode === 9) {
        if (self.attr('aria-expanded') == 'true') {
          if (!event.shiftKey) {
            event.preventDefault();
            this.$navFirstLink.focus();
          } else {
            if (event.shiftKey) {
              event.preventDefault();
              this.$content.focus();
            }
          }
        }
      }
    }
  }
  
}(jQuery);

Nav.init();

This awesome jQuery plugin is developed by dapacreative. For more Advanced Usages, please check the demo page or visit the official website.