Offcanvas

<div class="js-offcanvas">
    <div class="offcanvas">
        <div class="offcanvas__pusher">
            INSERT HEADER
            <div class="offcanvas__content">
                <div class="offcanvas__content-inner">
                    <button class="offcanvas__close"><svg class="icon icon--close ">
                            <use xlink:href="#icon--close" />
                        </svg>
                        Close</button>
                    INSERT NAV
                </div>
            </div>
            <button class="offcanvas__opener"><svg class="icon icon--menu ">
                    <use xlink:href="#icon--menu" />
                </svg>
                Menu</button>
            INSERT MAIN
            <div class="offcanvas__overlay"></div>
        </div>
    </div>
</div>
<div class="js-offcanvas">
  <div class="offcanvas">
    <div class="offcanvas__pusher">
      INSERT HEADER
      <div class="offcanvas__content">
        <div class="offcanvas__content-inner">
          <button class="offcanvas__close">{{> @icon name="close"}} Close</button>
          INSERT NAV
        </div>
      </div>
      <button class="offcanvas__opener">{{> @icon name="menu"}}Menu</button>
      INSERT MAIN
      <div class="offcanvas__overlay"></div>
    </div>
  </div>
</div>
/* No context defined for this component. */
  • Content:
    $offcanvas__width: rem(300);
    
    .offcanvas {
      position: relative;
      height: 100%;
      overflow-x: hidden;
    }
    
    .offcanvas__pusher {
      height: 100%;
      position: relative;
      left: 0;
      transition: transform 0.3s ease;
      transform: translate3d(0, 0, 0);
      backface-visibility: hidden;
    }
    
    .offcanvas__overlay {
      position: absolute;
      top: 0;
      right: 0;
      width: 0;
      height: 0;
      background: rgba($color__dark, 0.4);
      opacity: 0;
      z-index: 100;
      transition: opacity 0.2s, width 0.1s 0.2s, height 0.1s 0.2s;
    }
    
    .offcanvas__content {
      position: fixed;
      top: 0;
      right: 0;
      left: auto;
      overflow-y: auto;
      width: $offcanvas__width;
      height: 100%;
      z-index: 101;
      background-color: $color__brand--one;
      color: $body__font-color;
      backface-visibility: hidden;
      transform: translate3d(100%, 0, 0);
    
      .no-js & {
        position: static;
        width: 100%;
        transform: none;
      }
    }
    
    .offcanvas__content-inner {
      opacity: 0.7;
      transition: opacity 300ms 100ms ease, transform 400ms ease;
      transform: translate3d(-$offcanvas__width, 0, 0) scale3d(0.8, 0.8, 0.8);
      transform-origin: 50% 0%;
    
      .no-js & {
        opacity: 1;
        transform: none;
      }
    }
    
    .offcanvas__opener,
    .offcanvas__close {
      display: inline-block;
      background-color: transparent;
      border: 0;
      outline: 0;
      line-height: 1.9;
      padding: rem(5) 0;
      cursor: pointer;
    
      .icon {
        vertical-align: top;
      }
    }
    
    .offcanvas__opener {
      color: $color__bright;
      margin-right: rem(10);
    
      &:hover {
        color: $color__dark;
      }
    }
    
    .offcanvas__close {
      width: 100%;
      padding: rem(11) rem(12);
      text-align: left;
    
      &:hover {
        color: $color__bright;
      }
    }
    
    // STATES
    .offcanvas--left { // add class to body
      .offcanvas__content {
        transform: translate3d(-100%, 0, 0);
        left: 0;
        right: auto;
      }
    }
    
    .offcanvas--open {
      .offcanvas {
        overflow: hidden;
      }
    
      .offcanvas__pusher {
        transform: translate3d(-$offcanvas__width, 0, 0) scale3d(1, 1, 1);
      }
    
      &.offcanvas--left {
        .offcanvas__pusher {
          transform: translate3d($offcanvas__width, 0, 0) scale3d(1, 1, 1);
        }
      }
    
      .offcanvas__content {
        -webkit-overflow-scrolling: touch;
      }
    
      .offcanvas__content-inner {
        transform: translate3d(0, 0, 0);
        opacity: 1;
      }
    
      .offcanvas__overlay {
        transition: opacity 0.2s;
        width: 100%;
        height: 100%;
        opacity: 1;
      }
    
      .offcanvas__opener {
        transition: opacity 0.3s ease;
        opacity: 0;
        pointer-events: none;
      }
    }
    
    // NO-JS FALLBACK
    .no-js {
      .offcanvas__opener,
      .offcanvas__close {
        display: none;
      }
    }
    
  • URL: /components/raw/offcanvas/_offcanvas.scss
  • Filesystem Path: patterns/03-organisms/offcanvas/_offcanvas.scss
  • Size: 2.5 KB
  • Content:
    import Base from '../../_config/base';
    
    class Offcanvas extends Base {
      constructor(el) {
        super(el);
    
        this.config = {
          openClass: 'offcanvas--open',
          overlaySelector: '.offcanvas__overlay',
          openButtonSelector: '.offcanvas__opener',
          closeButtonSelector: '.offcanvas__close',
        };
    
        const togglerOpen = this.$el.querySelector(this.config.openButtonSelector);
        const togglerClose = this.$el.querySelector(this.config.closeButtonSelector);
        const overlay = this.$el.querySelector(this.config.overlaySelector);
    
        togglerOpen.addEventListener('click', this.toggle.bind(this));
        togglerClose.addEventListener('click', this.toggle.bind(this));
        overlay.addEventListener('click', this.toggle.bind(this));
      }
    
      toggle() {
        this.$el.classList.toggle(this.config.openClass);
      }
    }
    
    Offcanvas.className = 'Offcanvas';
    export default Offcanvas;
    
  • URL: /components/raw/offcanvas/offcanvas.js
  • Filesystem Path: patterns/03-organisms/offcanvas/offcanvas.js
  • Size: 887 Bytes

Documentation

This component is a combination of the <header class="header" /> and all containers & buttons for the off-canvas part.

  • small and middle screen => off-canvas navigation
  • large screens => default navigation/header

DOM

The DOM of the page should look like this (example is simplified):

<body class="js-offcanvas">
  <div class="offcanvas">
    <div class="offcanvas__pusher">

      <header class="header">

        <div class="offcanvas__content">
          <button class="offcanvas__close"></button>

          <div class="header__nav">
            <nav class="main-nav"> main navigation...</nav>
          </div>

          <div class="header__meta">
            meta-nav...
          </div>

        </div>

        <button class="offcanvas__opener"></button>

      </header>

      <main> Her comes the content...</main>
      <footer>footer of page</footer>

      <div class="offcanvas__overlay">
    </div>
  </div>
</body>

JS

The class js-offcanvas is to initialize the off-canvas functions (toggle with buttons). Toggle Buttons needs the class js-offcanvas__toggler for the eventhandler.