3 minute read

Having menus that gracefully adapt to the size of your screen can be troublesome. Inspired by the collapsible menu of Bootstrap, here is a pure CSS implementation using the checkbox trick to toggle visibility.

The example attached features a smaller menu when the screen is narrower than 900px and is collapsed then narrower than 650px. I encourage you to test whatever suits you best.

On collapse toggle, the height and visibility are animated for a smooth transition. height: auto; is not directly supported, so we instead animate the max-height property. The downside is that you need to specify the max-height your menu will ever have. If you set this too high, you will experience a slight graphic glitch.

The toggle handle is also inspired from Bootstrap, but with minimal markup.

* the :checked selector is not supported prior to IE9, you may use a polyfill like http://selectivizr.com/ if you need to.

Happy coding !

To test it, resize your browser.

Live demo on CodePen.io

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- responsive-menu.html -->
<input type="checkbox" id="navbar-checkbox" class="navbar-checkbox">
<nav class="menu">
  <ul>
    <li>Home</li>
    <li>About us</li>
    <li>Our company</li>
    <li>Our team</li>
    <li>Contact us</li>
  </ul>
  
  <label for="navbar-checkbox" class="navbar-handle"></label>
</nav>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// responsive-menu.less
// Smaller menu when on small screen
// All padding and margin are in em, so they will scale as well
@media (min-width : 900px) {
  .menu {
    font-size: 1.2em;
  }
}

.menu {
  padding: 0.5em;
  background: #eee;
  min-height: 2em;
  line-height: 1em;

  ul {
    transition: max-height .25s linear;
    margin: 0;
    padding: 0;
    text-align: center;
  }
  li {
    // visibility transition is on li because multiple transition selectors is not well supported
    transition: visibility .25s linear;
    display: inline-block;
    border: 1px solid;
    padding: .45em 1.1em;
    margin: 0 .3em;
  }
}

@media (max-width : 650px) {
  .menu {
    ul {
      max-height: 0;
      overflow: hidden;
      margin: 0 3.5em 0 1em;
    }

    li {
      visibility: hidden;
      display: block;
      padding: 0.5em 0.6em;
      border: none;
    }

    .navbar-handle {
      display: block;
    }
  }
  
  #navbar-checkbox:checked + .menu {
    ul {
      max-height: 300px; // Set this to the maximum height your menu will ever have.
    }
    
    li {
      visibility: visible;
    }
    .navbar-handle {
      &, &:after, &:before {
        border-color: #aaa;
      }
    }
  }
}

.navbar-checkbox {
    display: none;
}

// Will scale based on font-size
// Appears as 3 parallel horizontal bars
.navbar-handle {
    @height: 45px; // just a reference to compute em values on the fly
    display: none;
    cursor: pointer;
    position: relative;
    font-size: @height;
    padding: .5em 0;
    height: 0;
    width: 1em * 75px / @height;
    border-top: (1em * 6px / @height) solid;

    &:before, &:after {
        position: absolute;
        left: 0;
        right: 0;
        content: ' ';
        border-top: (1em * 6px / @height) solid;
    }

    &:before {
        top: 1em * 17px / @height;
    }

    &:after {
        top: 1em * 40px / @height;
    }
}



///////////

.menu {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  
  .navbar-handle {
    position: absolute;
    font-size: 1.2em;
    top: 0.7em;
    right: 12px;
    z-index: 10;    
  }
}

View Gist