Two Menus, One Responsive Off-Canvas Navigation

two menus combined into one off-canvas nav

I created this menu at work, when we wanted to have two menus but just one off-canvas navigation after the collapse. The idea was to combine an off-canvas menu with a classic navbar and then merge them into one separate off-canvas navigation on the collapse. Suprisingly, this proved to be not as easy as I initially had thought. But it was still achieved in the end, here you’ll see how.

See the Pen Two Menus, One responsive Off-Canvas by Myri (@mynimi) on CodePen.

Markup

We start with general markup. As already said in another tutorial I usually place my menus within a header so that’s what I did here as well. Then we have one container for both menus and each menu in a separate container with a list inside, since this is a dropdown. The toggle links to open the off canvas navs as well as the triangles to toggle the sub menues are going to be added via jQuery. I wanted the main menu to be aligned on the right and the toggle button on the left. To achieve that I had my toggle in a separate ul with the class first and the main menu in a list with the class second

Markup in Jade

    header
        .menu-container
            nav.off-canvas-menu
                ul
                    li
                        a(href="#") Tasum
                    li
                        a(href="#") Aldpol

                    li.menu-item-has-children
                        a(href="#") Oughw
                        ul.sub-menu
                            li
                                a(href="#") Shyidra
                            li
                                a(href="#") Randel
                            li
                                a(href="#") Ia'usk
                        // /.sub-menu

                    li
                        a(href="#") Swaimim
                // /ul
            // /.off-canvas-menu

            nav.main-menu
                ul.second
                    li
                        a(href="#") Smedgone
                    li
                        a(href="#") Aotaliope

                    li.menu-item-has-children
                        a(href="#") Rothiope
                        ul.sub-menu
                            li
                                a(href="#") Delirious Lyric
                            li
                                a(href="#") Cleliopyte
                            li
                                a(href="#") Xioeleia
                        // /.sub-menu

                    li
                        a(href="#") Iorireto    
                // /ul
            // /.main-menu
        // /.menu-container
    // /header

And the compiled HTML of that:

    <header>
        <div class="menu-container">
            <nav class="off-canvas-menu">
                <ul>
                    <li><a href="#">Tasum</a>
                    </li>
                    <li><a href="#">Aldpol</a>
                    </li>
                    <li class="menu-item-has-children"><a href="#">Oughw</a>
                        <ul class="sub-menu">
                            <li><a href="#">Shyidra</a>
                            </li>
                            <li><a href="#">Randel</a>
                            </li>
                            <li><a href="#">Ia'usk</a>
                            </li>
                        </ul>
                        <!-- /.sub-menu-->
                    </li>
                    <li><a href="#">Swaimim</a>
                    </li>
                </ul>
                <!-- /ul-->
            </nav>
            <!-- /.off-canvas-menu-->
            <nav class="main-menu">
                <ul class="second">
                    <li><a href="#">Smedgone</a>
                    </li>
                    <li><a href="#">Aotaliope</a>
                    </li>
                    <li class="menu-item-has-children"><a href="#">Rothiope</a>
                        <ul class="sub-menu">
                            <li><a href="#">Delirious Lyric</a>
                            </li>
                            <li><a href="#">Cleliopyte</a>
                            </li>
                            <li><a href="#">Xioeleia</a>
                            </li>
                        </ul>
                        <!-- /.sub-menu-->
                    </li>
                    <li><a href="#">Iorireto    </a>
                    </li>
                </ul>
                <!-- /ul-->
            </nav>
            <!-- /.main-menu-->
        </div>
        <!-- /.menu-container-->
    </header>
    <!-- /header-->

jQuery

We will work on toggles with jQuery. So add jQuery before you add the following script

    $(document).ready(function() {
        var offCanvasToggle = 'toggle-link-off-canvas',
            mainToggle = 'toggle-link',
            close = 'toggle-link-close';

        // add toggle links
        $('header').prepend('<span class="toggle ' + mainToggle + '">Menu</span>');
        $('.menu-container').prepend('<span class="toggle ' + mainToggle + ' ' + close + '">close</span>');
        $('.off-canvas-menu').prepend('<span class="toggle ' + offCanvasToggle + ' ' + close + '">close</span>');
        $('.main-menu').prepend('<ul class="first"><li><span class="toggle ' + offCanvasToggle + '">Menu Off-Canvas</span></li></ul>');

        // click functions for toggle links to toggle active classes on off-canvas
        $('header .toggle-link').click(function() {
            $('.menu-container').toggleClass('active');
        });

        $('header .toggle-link-off-canvas').click(function() {
            $('.off-canvas-menu').toggleClass('active');
        });

        // add Element for caret to toggle sub-menus
        $('.menu-item-has-children > a').wrap('<span class="parent"></span>');
        $('.menu-container .parent').append('<span class="submenu-opener">open submenu</span>');

        // toggling submenus
        $('.menu-container .submenu-opener').click(function() {
            $(this).toggleClass('sub-menu-is-open');
            $(this).parent().next('.sub-menu').slideToggle();
        });
    });

As soon as that’s done, we can add the styles.

Styling

As always I will use Sass. I use variables for the settings so feel free to play around.

Comments will go a little more into detail what everything does

    // SETTINGS
    //-----------

    // helper
    //--------

    // colors
    $pink: #E9B4B3;
    $pale-pink: #F2E4E1;
    $coal: #1D2426;
    $teal: #35565D;
    $grass: #8AB283;

    // shortcuts
    $noborder: 0 solid transparent;

    // variable definitions
    //----------------------
    $menu-collapse: 610px;
    $a_color: $pale-pink;
    $a_color_hover: $pink;
    $header_bg: $grass;
    $off-canvas_bg: $teal;
    $main_menu_bg: $coal;


    // FUNCTIONS
    //-----------

    // squareroot
    @function sqrt($r) {
        $x0: 1;
        $x1: $x0;
        @for $i from 1 through 10 {
            $x1: $x0 - ($x0 * $x0 - abs($r)) / (2 * $x0);
            $x0: $x1;
        }
        @return $x1;
    }

    // MIXINS
    //--------

    // equilateral triangle
    @mixin triangle_eqla($sl, $dir, $c) {
        width: 0;
        height: 0;
        border-style: solid;

        @if $dir == right {
            border-color: transparent transparent transparent $c;
        }
        @if $dir == left {
            border-color: transparent $c transparent transparent;
        }
        @if $dir == bottom or $dir == down {
            border-color: $c transparent transparent transparent;
        }
        @if $dir == top or $dir == up {
            border-color: transparent transparent $c transparent;
        }
        @if $dir == right or $dir == left {
            border-width: ($sl/2) (sqrt(3)*($sl/2));
        }
        @if $dir == top or $dir == down or $dir == bottom or $dir == up {
            border-width: (sqrt(3)*($sl/2)) ($sl/2);
        }
    }

    // flexbox
    @mixin flexbox($direction, $wrap, $justify, $align) {
        display: flex;
        flex-direction: $direction;
        flex-wrap: $wrap;
        justify-content: $justify;
        align-items: $align;
    }

    // STYLING
    //---------

    // basic styles
    body {
        font: {
            size: 16px;
            weight: 400;
            family: Roboto;
        }
        line-height: 1.5;
    }

    a {
        color: $a_color;
        text-decoration: none;
        &:hover {
            color: $a_color_hover;
            cursor: pointer;
        }
    }

    // main style
    header {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        background: $header_bg;
        line-height: 2;

        @media screen and (max-width: $menu-collapse){
            @include flexbox(row, wrap, center, center);
        }

        // toggle link styling
        .toggle {
            @extend a;
            padding: 6px;
        }

        .toggle-link-close {
            position: absolute;
            top: 0;
            right: 0;
        }

        .toggle-link {
            @media screen and (min-width: $menu-collapse + 1) {
                display: none;
            }
        }

        .toggle-link-off-canvas {
            @media screen and (max-width: $menu-collapse) {
                display: none;
            }
        }
    }

    // menu container, styles will be applied for the responsive version especially
    .menu-container {
        @media screen and (max-width: $menu-collapse) {
            z-index: 100;
            position: fixed;
            top: 0;
            left: -300px;
            height: 100%;
            overflow-y: auto;
            background: $off-canvas_bg;
            width: 300px;
            box-sizing: border-box;
            transition: .3s all ease;
            transform: translate(0, 0);

            &.active {
                transform: translate(300px, 0);
            }
        }

        // sub menu styles, are the same as with the off-canvas styling
        .sub-menu {
            border-top: 1px solid rgba($coal, .5);
            border-bottom: 1px solid rgba($coal, .5);
            background: lighten($off-canvas_bg, 5%);
            display: none;

            li {
                a {
                    box-sizing: border-box;
                    @media screen and (max-width: $menu-collapse) {
                        padding-left: 25px;
                    }
                }
            }
        }

        // clickable triangle to toggle sub menu visibility
        .parent {
            @include flexbox(row, wrap, flex-start, center);

            .submenu-opener {
                font-size: 0;
                position: relative;
                width: 20px;
                height: 20px;
                transition: .6s all ease;

                @media screen and (max-width: $menu-collapse) {
                    margin-left: 20px;
                }

                @media screen and (min-width: $menu-collapse + 1) {
                    margin-left: 5px;
                }

                &.sub-menu-is-open {
                    transform: rotate(-180deg);
                }

                &:after {
                    content: '';
                    @include triangle_eqla(10px, down, $a_color);
                    position: absolute;
                    top: 50%;
                    left: 50%;
                    transform: translate(-50%, -20%);
                    transition: .3s all ease;
                }

                &:hover {
                    cursor: pointer;

                    &:after {
                        border-color: $a_color_hover transparent transparent transparent;
                    }
                }
            }
        }
    }

    // off-canvas navigation
    .off-canvas-menu {
        line-height: 2.5;

        @media screen and (min-width: $menu-collapse + 1) {
            z-index: 100;
            position: fixed;
            top: 0;
            left: -300px;
            height: 100%;
            overflow-y: auto;
            background: $off-canvas_bg;
            width: 300px;
            box-sizing: border-box;
            transition: .3s all ease;
            transform: translate(0, 0);

            &.active {
                transform: translate(300px, 0);
            }        
        }

        a {
            padding: 0 10px;
        }
    }

    // main menu for responsive and full version
    .main-menu {
        @media screen and (max-width: $menu-collapse) {
            border-top: 1px solid darken($off-canvas_bg, 5%);
            line-height: 2.5;

            a {
                padding: 0 10px;
            }
        }

        @media screen and (min-width: $menu-collapse + 1) {
            position: fixed;
            width: 100%;
            left: 0;
            right: 0;
            background: $main_menu_bg;
            @include flexbox(row, wrap, space-between, center);

            ul {
                @include flexbox(row, wrap, flex-start, center);

                li {
                    padding: 10px;
                }
            }

            .sub-menu {
                top: 100%;
                position: absolute;
                background: lighten($main_menu_bg, 5%);
            }
        }    
    }

And the compiled CSS of that:

    body {
      font-size: 16px;
      font-weight: 400;
      font-family: Roboto;
      line-height: 1.5;
    }

    a, header .toggle {
      color: #F2E4E1;
      text-decoration: none;
    }
    a:hover, header .toggle:hover {
      color: #E9B4B3;
      cursor: pointer;
    }

    header {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      background: #8AB283;
      line-height: 2;
    }
    @media screen and (max-width: 610px) {
      header {
        display: -webkit-box;
        display: -webkit-flex;
        display: -ms-flexbox;
        display: flex;
        -webkit-box-orient: horizontal;
        -webkit-box-direction: normal;
        -webkit-flex-direction: row;
            -ms-flex-direction: row;
                flex-direction: row;
        -webkit-flex-wrap: wrap;
            -ms-flex-wrap: wrap;
                flex-wrap: wrap;
        -webkit-box-pack: center;
        -webkit-justify-content: center;
            -ms-flex-pack: center;
                justify-content: center;
        -webkit-box-align: center;
        -webkit-align-items: center;
            -ms-flex-align: center;
                align-items: center;
      }
    }
    header .toggle {
      padding: 6px;
    }
    header .toggle-link-close {
      position: absolute;
      top: 0;
      right: 0;
    }
    @media screen and (min-width: 611px) {
      header .toggle-link {
        display: none;
      }
    }
    @media screen and (max-width: 610px) {
      header .toggle-link-off-canvas {
        display: none;
      }
    }

    @media screen and (max-width: 610px) {
      .menu-container {
        z-index: 100;
        position: fixed;
        top: 0;
        left: -300px;
        height: 100%;
        overflow-y: auto;
        background: #35565D;
        width: 300px;
        box-sizing: border-box;
        -webkit-transition: .3s all ease;
                transition: .3s all ease;
        -webkit-transform: translate(0, 0);
            -ms-transform: translate(0, 0);
                transform: translate(0, 0);
      }
      .menu-container.active {
        -webkit-transform: translate(300px, 0);
            -ms-transform: translate(300px, 0);
                transform: translate(300px, 0);
      }
    }
    .menu-container .sub-menu {
      border-top: 1px solid rgba(29, 36, 38, 0.5);
      border-bottom: 1px solid rgba(29, 36, 38, 0.5);
      background: #3e656d;
      display: none;
    }
    .menu-container .sub-menu li a, .menu-container .sub-menu li header .toggle, header .menu-container .sub-menu li .toggle {
      box-sizing: border-box;
    }
    @media screen and (max-width: 610px) {
      .menu-container .sub-menu li a, .menu-container .sub-menu li header .toggle, header .menu-container .sub-menu li .toggle {
        padding-left: 25px;
      }
    }
    .menu-container .parent {
      display: -webkit-box;
      display: -webkit-flex;
      display: -ms-flexbox;
      display: flex;
      -webkit-box-orient: horizontal;
      -webkit-box-direction: normal;
      -webkit-flex-direction: row;
          -ms-flex-direction: row;
              flex-direction: row;
      -webkit-flex-wrap: wrap;
          -ms-flex-wrap: wrap;
              flex-wrap: wrap;
      -webkit-box-pack: start;
      -webkit-justify-content: flex-start;
          -ms-flex-pack: start;
              justify-content: flex-start;
      -webkit-box-align: center;
      -webkit-align-items: center;
          -ms-flex-align: center;
              align-items: center;
    }
    .menu-container .parent .submenu-opener {
      font-size: 0;
      position: relative;
      width: 20px;
      height: 20px;
      -webkit-transition: .6s all ease;
              transition: .6s all ease;
    }
    @media screen and (max-width: 610px) {
      .menu-container .parent .submenu-opener {
        margin-left: 20px;
      }
    }
    @media screen and (min-width: 611px) {
      .menu-container .parent .submenu-opener {
        margin-left: 5px;
      }
    }
    .menu-container .parent .submenu-opener.sub-menu-is-open {
      -webkit-transform: rotate(-180deg);
          -ms-transform: rotate(-180deg);
              transform: rotate(-180deg);
    }
    .menu-container .parent .submenu-opener:after {
      content: '';
      width: 0;
      height: 0;
      border-style: solid;
      border-color: #F2E4E1 transparent transparent transparent;
      border-width: 8.66025px 5px;
      position: absolute;
      top: 50%;
      left: 50%;
      -webkit-transform: translate(-50%, -20%);
          -ms-transform: translate(-50%, -20%);
              transform: translate(-50%, -20%);
      -webkit-transition: .3s all ease;
              transition: .3s all ease;
    }
    .menu-container .parent .submenu-opener:hover {
      cursor: pointer;
    }
    .menu-container .parent .submenu-opener:hover:after {
      border-color: #E9B4B3 transparent transparent transparent;
    }

    .off-canvas-menu {
      line-height: 2.5;
    }
    @media screen and (min-width: 611px) {
      .off-canvas-menu {
        z-index: 100;
        position: fixed;
        top: 0;
        left: -300px;
        height: 100%;
        overflow-y: auto;
        background: #35565D;
        width: 300px;
        box-sizing: border-box;
        -webkit-transition: .3s all ease;
                transition: .3s all ease;
        -webkit-transform: translate(0, 0);
            -ms-transform: translate(0, 0);
                transform: translate(0, 0);
      }
      .off-canvas-menu.active {
        -webkit-transform: translate(300px, 0);
            -ms-transform: translate(300px, 0);
                transform: translate(300px, 0);
      }
    }
    .off-canvas-menu a, .off-canvas-menu header .toggle, header .off-canvas-menu .toggle {
      padding: 0 10px;
    }

    @media screen and (max-width: 610px) {
      .main-menu {
        border-top: 1px solid #2c474d;
        line-height: 2.5;
      }
      .main-menu a, .main-menu header .toggle, header .main-menu .toggle {
        padding: 0 10px;
      }
    }
    @media screen and (min-width: 611px) {
      .main-menu {
        position: fixed;
        width: 100%;
        left: 0;
        right: 0;
        background: #1D2426;
        display: -webkit-box;
        display: -webkit-flex;
        display: -ms-flexbox;
        display: flex;
        -webkit-box-orient: horizontal;
        -webkit-box-direction: normal;
        -webkit-flex-direction: row;
            -ms-flex-direction: row;
                flex-direction: row;
        -webkit-flex-wrap: wrap;
            -ms-flex-wrap: wrap;
                flex-wrap: wrap;
        -webkit-box-pack: justify;
        -webkit-justify-content: space-between;
            -ms-flex-pack: justify;
                justify-content: space-between;
        -webkit-box-align: center;
        -webkit-align-items: center;
            -ms-flex-align: center;
                align-items: center;
      }
      .main-menu ul {
        display: -webkit-box;
        display: -webkit-flex;
        display: -ms-flexbox;
        display: flex;
        -webkit-box-orient: horizontal;
        -webkit-box-direction: normal;
        -webkit-flex-direction: row;
            -ms-flex-direction: row;
                flex-direction: row;
        -webkit-flex-wrap: wrap;
            -ms-flex-wrap: wrap;
                flex-wrap: wrap;
        -webkit-box-pack: start;
        -webkit-justify-content: flex-start;
            -ms-flex-pack: start;
                justify-content: flex-start;
        -webkit-box-align: center;
        -webkit-align-items: center;
            -ms-flex-align: center;
                align-items: center;
      }
      .main-menu ul li {
        padding: 10px;
      }
      .main-menu .sub-menu {
        top: 100%;
        position: absolute;
        background: #283234;
      }
    }

And that’s it.