Basic responsive menu (off-canvas with flexbox)

pretty much the easiest 1-Level-Menu ever

Menus. The first Menu I wrote was a list that was made looking like a navigation with CSS. I still use a lisst for a drop down menu. But since we are finally ready to use Flexbox, I have given up on lists and turned to a simpler markip. This navigation is written with flexbos and becomes off-canvas once it collapses.

See the Pen responsive 1-Level Menu by Myri (@mynimi) on CodePen.

I use Font-Awesome and jQuery here because I always have those in my projectrs. But you can create a menu like that, with CSS only. If you are interested in a tutorial, let me know.

Markup

Since I use my menus in the header area that’s what I did here as well, because I did not want my toggle link floating arount in the emptyness. There are two. One within the nav element, that I use to wrap the items and one within. They will toggle the off-canvas. So only used for that version.

And so that markup is written a little faster, I will use jade

    header
        a.toggle-link(href='#menu')
            | <i class="fa fa-navicon"></i> Menu
        nav.main-menu#menu
            a.toggle-link(href='#menu')
                i.fa.fa-close
            each val in [1, 2, 3, 4, 5]
                a(href='index'+val+'.html')= 'Item ' + val

This will result in the following HTML

    <header>
        <a href="#menu" class="toggle-link">
            <i class="fa fa-navicon"></i> Menu
        </a>
        <nav id="menu" class="main-menu">
            <a href="#menu" class="toggle-link">
                <i class="fa fa-close"></i>
            </a>
            <a href="index1.html">Item 1</a>
            <a href="index2.html">Item 2</a>
            <a href="index3.html">Item 3</a>
            <a href="index4.html">Item 4</a>
            <a href="index5.html">Item 5</a>
      </nav>
    </header>

Styling

Styling is done with Flexbox. And Sass, because I will never write plain CSS again. Comments will explain a bit further.

    // point at which menu turns to off-canvas
    $menu_collapse: 500px;

    // basic styling
    body {
        font: {
            size: 22px;
            weight: 300;
            family: Roboto;
        }
        line-height: 1.8;
        color: #333;
    }

    header{
        width: 100%;
        position: relative;
        left: 0;
        top: 0;
        background: darken(white, 15%);
    }
    a {
        text-decoration: none;
        color: black;
        transition: .3s;
        &:hover {
            color: lighten(black, 50%);
        }
    }

    // menu style
    .main-menu {
        display: flex;
        flex-wrap: wrap;
        align-items: center;

        // change the way links appear with flex-direction

        // on a horizontal line in non-collapsed way
        @media screen and (min-width: $menu_collapse) {
            flex-direction: row;
            justify-content: space-between;
            max-width: 800px;

            .toggle-link {
                display: none;
            }
        }

        // off-canvas look
        @media screen and (max-width: $menu_collapse) {
            // make links appear underneath each other
            flex-direction: column;
            justify-content: flex-start;

            // off-canvas style, closed
            background: darken(white, 10%);
            z-index: 100;
            width: 320px;
            min-height: 100%;
            position: fixed;
            top: 0;
            left: 0;
            transition: .3s all linear;
            box-sizing: border-box;
            padding: 10px;
            transform: translateX(-320px);

            // off-canvas, open
            &.active {
                transform: translateX(0px);
            }

            // position "x" in the upper right-hand corner
            .toggle-link {
                align-self: flex-end;
            }
        }
    }

    // make toggle links visible if off-canvas menu is to be displayed
    header {
        .toggle-link {
            @media screen and (min-width: $menu_collapse) {
                display: none;
            }
        }
    }

and that’s what the styles look like after sass and autoprefixer did their work

    .main-menu {
      display: -webkit-box;
      display: -webkit-flex;
      display: -ms-flexbox;
      display: flex;
      -webkit-flex-wrap: wrap;
          -ms-flex-wrap: wrap;
              flex-wrap: wrap;
      -webkit-box-align: center;
      -webkit-align-items: center;
          -ms-flex-align: center;
              align-items: center;
    }
    @media screen and (min-width: 500px) {
      .main-menu {
        -webkit-box-orient: horizontal;
        -webkit-box-direction: normal;
        -webkit-flex-direction: row;
            -ms-flex-direction: row;
                flex-direction: row;
        -webkit-box-pack: justify;
        -webkit-justify-content: space-between;
            -ms-flex-pack: justify;
                justify-content: space-between;
        max-width: 800px;
      }
      .main-menu .toggle-link {
        display: none;
      }
    }
    @media screen and (max-width: 500px) {
      .main-menu {
        -webkit-box-orient: vertical;
        -webkit-box-direction: normal;
        -webkit-flex-direction: column;
            -ms-flex-direction: column;
                flex-direction: column;
        -webkit-box-pack: start;
        -webkit-justify-content: flex-start;
            -ms-flex-pack: start;
                justify-content: flex-start;
        background: #e6e6e6;
        z-index: 100;
        width: 320px;
        min-height: 100%;
        position: fixed;
        top: 0;
        left: 0;
        -webkit-transition: .3s all linear;
                transition: .3s all linear;
        box-sizing: border-box;
        padding: 10px;
        -webkit-transform: translateX(-320px);
            -ms-transform: translateX(-320px);
                transform: translateX(-320px);
      }
      .main-menu.active {
        -webkit-transform: translateX(0px);
            -ms-transform: translateX(0px);
                transform: translateX(0px);
      }
      .main-menu .toggle-link {
        -webkit-align-self: flex-end;
            -ms-flex-item-align: end;
                align-self: flex-end;
      }
    }

    @media screen and (min-width: 500px) {
      header .toggle-link {
        display: none;
      }
    }

and a pinch of jQuery

For the off-canvas to function, we will write a click event, that triggers an active class to display the menu or hide it.

    $(document).ready(function () {
        $(".toggle-link").click(function () {
            $("#menu").toggleClass("active");
        });
    });

and that’s it. Super simple, really easy to customize.