The online home of Daniel Winer

CSS only responsive navigation Apr 21, 2013

Whilst building a Jquery plugin for responsive navigation I started to play around with a CSS only version, just to see what's possible and what we actually need the javascript for. I would say that this is a perfectly feasible solution for a production website's not plug and play, you have to learn how to use the code and adapt it to your site.

Is CSS only feasible?

The good

Responsive solutions are modern solutions by default, check out browser support for media queries and you will see that we don't need to worry about old browsers. This is really important because the solution I propose relies on the :checked pseudo-class which is supported in these same browsers. The animation of the slide out panel that houses the links for the responsive menu uses transform and transition which are more or less supported in the same browsers. I say more or less because IE9 is the weak link here considering that it does support media queries and it does support :checked but it doesn't support transitions. The majority of your mobile users are probably going to be on webkit but for those on Windows Phone 7 (and therefore IE9) it might not look so smooth but should be functional.

The bad

There are a few features that the Jquery plugin will have that just can't work in the CSS only solution, or at least I can't figure out a way to make them work! I won't list all the features of the Jquery plugin I am working on just yet but there are some issues with the CSS only solution that will be resolved with a bit of Javascript:

There a few more advantages of the Jquery solution but that will be explained in the next article.

How does it work?

First of all we have a show/hide menu toggle, which in this case is a checkbox that has been hidden by a font icon placed over the top using the label:after pseudo element. The toggle is hidden by default and only shown on smaller screens. By using the :checked pseudo class we can change the icon that is displayed once the checkbox has been clicked, in this case we are going to turn it into a "close" icon. For this to work the checkbox must be placed before the <label> because we are relying on this sibling selector to change the icon:

.slide-checkbox:checked ~ .slide-toggle:after {
  content: '"';

.slide-checkbox is the checkbox, .slide-toggle is the <label>

The cool part is the slide out menu, in fact it's not really the menu that slides out but more the content that slides over to the right, dragging the menu along with it. In the media query the menu is hidden off-canvas using this CSS:

.nav {
  width: 70%;
  position: absolute;
  left: 0;
  top: 0;
  transform: translateX(70%) scale(1);

(This has been shortened for readibility, in the demo you'll see the full code with browser prefixes.)

Now, the important part is sliding the content over. To do this I am relying on the content to be inside a <div class="slide"> and to follow the checkbox in the HTML markup because of this CSS:

.slide-checkbox:checked ~ .slide {
  transform: translateX(70%) scale(1);

What this is saying is: "when the .slide-toggle is checked, then any div with a class of "slide" that is adjacent in the markup should be transformed". When we slide the div over we also bring the nav into view.

Another important aspect is that everything, including the checkbox, is wrapped inside <div class="outer"> which has overflow: hidden set and this stops scrollbars appearing once the content slides over.

Here's a breakdown of the necessary HTML structure:

  <div class="outer">
    <input type="checkbox" id="slide-checkbox" class="slide-checkbox" role="button">
    <label for="slide-checkbox" class="slide-toggle" onclick></label>
    <div class="slide">
      <ul class="nav">
        <!-- menu content here -->
      <!-- add any content you want in here -->

Something that is pretty flexible is the .nav, this can be a <div> instead of a <ul> and you don't need to be limited to just putting a menu in there. Also, the nav can be pretty much anywhere inside the <div class="slide">, even inside other divs.

The best way to see what's going on though is to download the demo and have a look at the CSS because all the important parts are heavily commented. And, if you're interested in the Jquery solution when it comes out, which will have a range of configurable options, then follow on twitter and/or watch the project on github. Please let me know what you think in the comments.


Thanks to Marnu Lombard for creating a really useful pull request that made me change from translate3D to translateX and put the toggle on the label:after instead of the checkbox:after. That really helped browser compatibility.

Thanks to Ben and Dan "5 mins" Caragea for mobile testing.

comments powered by Disqus