Gracefully Shrinking Vertical Menu

16 Jan 2013

Media queries are great for slowly degrading elements as the screen resizes. Sass makes this even easier, letting you pepper in those media queries right in the middle of your code. Here's an example of an instance that this would be extremely useful.

We're going to build a responsive vertical menu that holds a lot of information, and as we are provided smaller amounts of space, we'll make sure that it gracefully minimizes itself. There is a sample at the bottom of the page if you'd like to see what we're creating before you get started.

Set Up The Basic Navigation

We'll get started with a basic navigation structure...

    <nav>
      <ul>
        <li>
          <a href="#">Home</a>
        </li>
        <li>
          <a href="#">About</a>
        </li>
        <li>
          <a href="#">Articles</a>
        </li>
        <li>
          <a href="#">Portfolio</a>
         </li>
        <li>
          <a href="#">Contact</a>
         </li>
      </ul>
    </nav>

...and then give it some basic styles and a nice hover transition to make it a little more aesthetically pleasing.

    nav {
       width:22em;
    }
    ul {
      list-style-type:none;
      width:100%;
    }
    li {
      padding:10px;
      border-radius:5px;
      border:1px solid #16525e;
      background-color:#22707f;
      transition: background-color .5s;
      -moz-transition: background-color .5s;
      -webkit-transition: background-color .5s;
      -o-transition: background-color .5s;

      &:hover {
        background-color:#36b2cc;
      }

      }
    }
    li a {
      text-decoration:none;
      color:#fff;
      padding-left:20px;

    }

Arrows

We now have a decent looking navigation, so let's add some more elements. I want to add some decorative arrows before each of the links, so we'll use the :before pseudo element to take care of that using only CSS.

Nest this inside your li so we don't have to worry about interfering with the links. This is why the padding-left:20px; was added to the li a.

    &:before {
        content: ' ';
        height: 0;
        position: absolute;
        width: 0;
        border: 10px solid transparent;
        border-left-color: #333;
    }

The arrow comes from the thick corner of the border that we styled. Since there isn't any actual content, the border sits alone.

Descriptions

Now we'll add some descriptions for each of our menu links to let the users know where each of them is going. We're going to break these descriptions twice, hence the two seperate spans. The .long span will be taken out quickly as it takes up a lot of space, then the rest will be taken out later. Spans are nothing more than inline divs, so don't worry about nesting them.

    <nav>
      <ul>
        <li>
          <a href="#">
            Home <span class="desc">- Welcome to my website<span class="long">, where you can find all of my information.</span></span>
           </a>
        </li>
        <li>
          <a href="#">
            About <span class="desc">- All about me<span class="long">.  Favorite things, what I do, where I am.</span></span>
           </a>
        </li>
        <li>
          <a href="#">
            Articles <span class="desc">- Read my posts<span class="long"> about things that interest me in my field.  I post daily and enjoy comments!</span></span>
           </a>
        </li>
        <li>
          <a href="#">
            Portfolio <span class="desc">- All of my projects<span class="long"> that I have done for clients and for myself.</span></span>
           </a>
        </li>
        <li>
          <a href="#">
            Contact <span class="desc">- Get in contact with me<span class="long"> through social media, email, or snail mail.</span></span>
           </a>
        </li>
      </ul>
    </nav>


    .desc {
      font-size:.8em;
      color:#3e3e3e;
    }

Now we have our full navigation that we're content with on the largest screens. full navigation

Media Queries

Now we have our full navigation, but as soon as the window starts to get smaller, it begins to break. To fix this, we'll use the "shrink it til it breaks method" of using break points that work with the content that we're using, instead of choosing break points based on targeting specific device resolutions.

Our first break point will be around 800px, so we'll start there. The menu is taking up too much of the screen, so we'll add our first media query to the nav and make the width smaller.

    nav {
       width:22em;
       @media (max-width: 800px) {
         width:15em;
        }
    }

Unfortunately, this shrinking of the nav will cause the text to flow to the next line, making our navigation look bloated. We prepared for this with our multiple <span>, so we'll hide the .long class to take away the extra text.

    .long {
      @media (max-width:800px) {
        display:none;
      }
    }

When the screen dips below this size, now our navigation will still look concise and together.

after one break point

The next break point we'll hit is 600px. The navigation is again getting too wide for our screen, so let's shrink it and then fix what breaks. Start with adding another media query to the nav.

    nav {
       width:22em;
       @media (max-width: 800px) {
         width:15em;
        }
       @media (max-width: 600px) {
         width:8em;
        }
    }

Now you'll notice that none of our description fits well next to the links, so let's remove them by adding a media query to .desc.

    .desc {
      font-size:.8em;
      color:#3e3e3e;
      @media (max-width: 600px) {
          display:none;
        }
    }

Our descriptions are now removed at this lower resolution, and the menu again looks as you would expect it to.

after two break points

Our navigation is now almost completely broken down, but at mobile-size resolutions we may still have issues. We'll take away the arrows and shorten it up so it can fit even on tiny screens. As before, we'll start with the nav.

    nav {
       width:22em;
       @media (max-width: 800px) {
         width:15em;
        }
       @media (max-width: 600px) {
         width:8em;
        }
       @media (max-width: 410px) {
         width:6em;
        }
    }

Now we'll take away the arrows and get rid of the border-radius so we can have our menu as tightened up as possible. Under your li selector, add a media query a little above 400px. We'll make it save space by lowering the padding, and take the border-radius away so it looks better being so small.

    @media (max-width: 410px) {
       padding:5px;
       border-radius:0;
    }

Getting rid of the arrows involves adding a media query that takes away the border that creates them. This is located in the :before pseudo selector, so put the media query there.

    &:before {
        content: ' ';
          height: 0;
          position: absolute;
          width: 0;
          border: 10px solid transparent;
          border-left-color: #333;
          @media (max-width: 410px) {
          border:none;
          }

    }

The navigation is now completely broken down at smaller screens. We can now be confident that no matter what the resolution our menu is displayed at, and no matter what device it's being viewed on, it will still look nice and function well.

fully shrunk

The Whole Thing

Here's the entirety of the code, and a Codepen example that you can try out yourself. Keep in mind that very little of this is set in stone. You can make it look however you want and adjust the media queries according to your specific content, this is just what happened to work for mine.

HTML5

    <nav>
      <ul>
        <li>
          <a href="#">
            Home <span class="desc">- Welcome to my website<span class="long">, where you can find all of my information.</span></span>
           </a>
        </li>
        <li>
          <a href="#">
            About <span class="desc">- All about me<span class="long">.  Favorite things, what I do, where I am.</span></span>
           </a>
        </li>
        <li>
          <a href="#">
            Articles <span class="desc">- Read my posts<span class="long"> about things that interest me in my field.  I post daily and enjoy comments!</span></span>
           </a>
        </li>
        <li>
          <a href="#">
            Portfolio <span class="desc">- All of my projects<span class="long"> that I have done for clients and for myself.</span></span>
           </a>
        </li>
        <li>
          <a href="#">
            Contact <span class="desc">- Get in contact with me<span class="long"> through social media, email, or snail mail.</span></span>
           </a>
        </li>
      </ul>
    </nav>

Sass

    nav {
       width:22em;
       @media (max-width: 800px) {
         width:15em;
        }
       @media (max-width: 600px) {
         width:8em;
        }
       @media (max-width: 410px) {
         width:6em;
        }
    }
    ul {
      list-style-type:none;
      width:100%;
    }
    li {
      padding:10px;
      border-radius:5px;
      border:1px solid #16525e;
      background-color:#22707f;
      transition: background-color .5s;
      -moz-transition: background-color .5s;
      -webkit-transition: background-color .5s;
      -o-transition: background-color .5s;


      @media (max-width: 410px) {
        padding:5px;
        border-radius:0;
        }

      &:hover {
        background-color:#36b2cc;
      }

      &:before {
        content: ' ';
          height: 0;
          position: absolute;
          width: 0;
          border: 10px solid transparent;
          border-left-color: #333;
        @media (max-width: 410px) {
          border:none;

        }

      }
    }
    li a {
      text-decoration:none;
      color:#fff;
      padding-left:20px;

    }
    .desc {
      font-size:.8em;
      color:#3e3e3e;
      @media (max-width: 600px) {
          display:none;
        }
    }
    .long {
      @media (max-width:800px) {
        display:none;
      }
    }

Example