Skip to main content

A Responsive Multi-Level-Menu without JavaScript

It’s been a while since I wrote something about doing JavaScript work with CSS. So I decided to create this responsive navigation using the :checked hack.

Classic Dropdown

Classic Dropdown

So we start with a basic dropdown code, that’s based on nested lists. I did 4 levels in this case, but you could extend it to however much you wanted.

1
<div class="menu">
2
<ul class="nav level-one">
3
<li>
4
<a href="#">Item 1</a>
5
</li>
6
<li class="parent">
7
<a href="#">Item 2</a>
8
<ul class="level-two">
9
<li>
10
<a href="#">Child 1</a>
11
</li>
12
<li>
13
<a href="#">Child 2</a>
14
</li>
15
<li class="parent">
16
<a href="#">Child 3</a>
17
<ul class="level-three">
18
<li>
19
<a href="#">Grandchild 1</a>
20
</li>
21
<li class="parent">
22
<a href="#">Grandchild 2</a>
23
<ul class="level-four">
24
<li>
25
<a href="#">Grandgrandchild 1</a>
26
</li>
27
<li>
28
<a href="#">Grandgrandchild 2</a>
29
</li>
30
<li>
31
<a href="#">Grandgrandchild 3</a>
32
</li>
33
</ul>
34
<!-- /.level-four -->
35
</li>
36
</ul>
37
<!-- /.level-three -->
38
</li>
39
</ul>
40
<!-- /.level-two -->
41
</li>
42
<li class="parent">
43
<a href="#">Item 3</a>
44
<ul class="level-two">
45
<li>
46
<a href="#">Child 1</a>
47
</li>
48
<li>
49
<a href="#">Child 2</a>
50
</li>
51
<li class="parent">
52
<a href="#">Child 3</a>
53
<ul class="level-three">
54
<li>
55
<a href="#">Grandchild 1</a>
56
</li>
57
<li>
58
<a href="#">Grandchild 2</a>
59
</li>
60
<li>
61
<a href="#">Grandchild 3</a>
62
</li>
63
</ul>
64
<!-- /.level-three -->
65
</li>
66
<li>
67
<a href="#">Child 4</a>
68
</li>
69
</ul>
70
<!-- /.level-two -->
71
</li>
72
<li>
73
<a href="#">Item 4</a>
74
</li>
75
<li>
76
<a href="#">Item 5</a>
77
</li>
78
<li>
79
<a href="#">Item 6</a>
80
</li>
81
</ul>
82
</div>

:checked hack

:checked hack

To open the submenus we’ll use checkboxes. If you don’t know how the checkbox hack works, just google it, there are some really good posts that go into much detail. But basically we will depend the visibility of the submenu on the state of the checkbox placed before it.

Markup

Markup

So we change our markup to look like this:

1
<div class="menu">
2
<ul class="nav level-one">
3
<li>
4
<a href="#">Item 1</a>
5
</li>
6
<li class="parent">
7
<a href="#">Item 2 </a>
8
<label for="toggle-level-2-01" class="toggle">toggle submenu</label>
9
<input type="checkbox" id="toggle-level-2-01"/>
10
<ul class="level-two">
11
<li>
12
<a href="#">Child 1</a>
13
</li>
14
<li>
15
<a href="#">Child 2</a>
16
</li>
17
<li class="parent">
18
<a href="#">Child 3</a>
19
<label for="toggle-level-3-01" class="toggle">toggle submenu</label>
20
<input type="checkbox" id="toggle-level-3-01"/>
21
<ul class="level-three">
22
<li>
23
<a href="#">Grandchild 1</a>
24
</li>
25
<li class="parent">
26
<a href="#">Grandchild 2</a>
27
<label for="toggle-level-4-01" class="toggle">toggle submenu</label>
28
<input type="checkbox" id="toggle-level-4-01"/>
29
<ul class="level-four">
30
<li>
31
<a href="#">Grandgrandchild 1</a>
32
</li>
33
<li>
34
<a href="#">Grandgrandchild 2</a>
35
</li>
36
<li>
37
<a href="#">Grandgrandchild 3</a>
38
</li>
39
</ul>
40
</li>
41
</ul>
42
</li>
43
</ul>
44
</li>
45
<li class="parent">
46
<a href="#">Item 3</a>
47
<label for="toggle-level-2-02" class="toggle">toggle submenu</label>
48
<input type="checkbox" id="toggle-level-2-02"/>
49
<ul class="level-two">
50
<li>
51
<a href="#">Child 1</a>
52
</li>
53
<li>
54
<a href="#">Child 2</a>
55
</li>
56
<li class="parent">
57
<a href="#">Child 3</a>
58
<label for="toggle-level-3-02" class="toggle">toggle submenu</label>
59
<input type="checkbox" id="toggle-level-3-02"/>
60
<ul class="level-three">
61
<li>
62
<a href="#">Grandchild 1</a>
63
</li>
64
<li>
65
<a href="#">Grandchild 2</a>
66
</li>
67
<li>
68
<a href="#">Grandchild 3</a>
69
</li>
70
</ul>
71
</li>
72
<li>
73
<a href="#">Child 4</a>
74
</li>
75
</ul>
76
</li>
77
<li>
78
<a href="#">Item 4</a>
79
</li>
80
<li>
81
<a href="#">Item 5</a>
82
</li>
83
<li>
84
<a href="#">Item 6</a>
85
</li>
86
</ul>
87
</div>

Open

Open

The opening will be done using this CSS

1
.menu ul > li ul {
2
display: none;
3
}
4
.menu ul > li input[type="checkbox"]:checked + ul {
5
display: block;
6
}

Styling

Styling

Now we’ll just have to style it. I went with a pretty standard bar.

1
.menu {
2
font-size: 16px;
3
font-family: Roboto Slab, serif;
4
background-color: #C06574;
5
color: white;
6
position: fixed;
7
top: 0;
8
left: 0;
9
width: 100%;
10
padding: 10px 0;
11
z-index: 10;
12
}
13
.menu input[type="checkbox"] {
14
opacity: 0;
15
position: absolute;
16
left: -9999px;
17
}
18
.menu a {
19
color: white;
20
text-decoration: none;
21
-webkit-transition: .3s all ease;
22
transition: .3s all ease;
23
}
24
.menu a:hover {
25
color: #E3DFE6;
26
}
27
.menu label.toggle {
28
color: transparent;
29
position: relative;
30
}
31
.menu label.toggle:hover {
32
cursor: pointer;
33
}
34
.menu label.toggle:hover:before {
35
width: 0;
36
height: 0;
37
border-style: solid;
38
border-color: transparent transparent transparent #E3DFE6;
39
border-width: 5.33333px;
40
}
41
.menu label.toggle:before {
42
content: "";
43
font-size: 16px;
44
-webkit-transition: .3s all ease;
45
transition: .3s all ease;
46
position: absolute;
47
top: 50%;
48
left: 5px;
49
-webkit-transform: translateY(0%) rotate(90deg);
50
transform: translateY(0%) rotate(90deg);
51
width: 0;
52
height: 0;
53
border-style: solid;
54
border-color: transparent transparent transparent white;
55
border-width: 5.33333px;
56
}
57
.menu ul {
58
position: relative;
59
}
60
.menu ul,
61
.menu ul li {
62
list-style: none;
63
padding: 0;
64
margin: 0;
65
}
66
.menu ul > li {
67
display: inline;
68
padding: 0 10px;
69
position: relative;
70
}
71
.menu ul > li > ul {
72
position: absolute;
73
top: calc(100% + 10px);
74
left: 0;
75
background: #C06574;
76
display: none;
77
}
78
.menu ul > li > ul > li {
79
display: block;
80
white-space: nowrap;
81
}
82
.menu ul > li > ul > li label.toggle:before {
83
-webkit-transform: translateY(-50%);
84
transform: translateY(-50%);
85
}
86
.menu ul > li > ul ul {
87
left: 100%;
88
top: 0;
89
}
90
.menu ul > li input[type="checkbox"]:checked + ul {
91
display: block;
92
}

And that will look like this:

See the Pen Checkbox-Hack Dropdown (non-responsive) by Myri ( @mynimi) on CodePen.

Responsive

Responsive

For the responsive version we’ll have to adjust some more of the markup. We add another checkbox to toggle the whole navigation. And then we just adjust the CSS to create this off-canvas Menu

And that’s what it looks like:

1
<div class="menu">
2
<label for="toggle-responsive" class="toggle-menu">Menu</label>
3
<input type="checkbox" id="toggle-responsive"/>
4
<ul class="nav level-one">
5
<li><a href="#">Item 1</a></li>
6
<li class="parent"><a href="#">Item 2 </a>
7
<label for="toggle-level-2-01" class="toggle">o</label>
8
<input type="checkbox" id="toggle-level-2-01"/>
9
<ul class="level-two">
10
<li><a href="#">Child 1</a></li>
11
<li><a href="#">Child 2</a></li>
12
<li class="parent"><a href="#">Child 3</a>
13
<label for="toggle-level-3-01" class="toggle">o</label>
14
<input type="checkbox" id="toggle-level-3-01"/>
15
<ul class="level-three">
16
<li><a href="#">Grandchild 1</a></li>
17
<li class="parent"><a href="#">Grandchild 2</a>
18
<label for="toggle-level-4-01" class="toggle">o</label>
19
<input type="checkbox" id="toggle-level-4-01"/>
20
<ul class="level-four">
21
<li><a href="#">Grandgrandchild 1</a></li>
22
<li><a href="#">Grandgrandchild 2</a></li>
23
<li><a href="#">Grandgrandchild 3</a></li>
24
</ul>
25
</li>
26
</ul>
27
</li>
28
</ul>
29
</li>
30
<li class="parent"><a href="#">Item 3</a>
31
<label for="toggle-level-2-02" class="toggle">o</label>
32
<input type="checkbox" id="toggle-level-2-02"/>
33
<ul class="level-two">
34
<li><a href="#">Child 1</a></li>
35
<li><a href="#">Child 2</a></li>
36
<li class="parent"><a href="#">Child 3</a>
37
<label for="toggle-level-3-02" class="toggle">o</label>
38
<input type="checkbox" id="toggle-level-3-02"/>
39
<ul class="level-three">
40
<li><a href="#">Grandchild 1</a></li>
41
<li><a href="#">Grandchild 2</a></li>
42
<li><a href="#">Grandchild 3</a></li>
43
</ul>
44
</li>
45
<li><a href="#">Child 4</a></li>
46
</ul>
47
</li>
48
<li><a href="#">Item 4</a></li>
49
<li><a href="#">Item 5</a></li>
50
<li><a href="#">Item 6</a></li>
51
</ul>
52
</div>

In the example I put the breakpoint at 2000px so that it shows up on all stanard displays. In real life you would have to adjust this, so that the non-responsive version will be shown first.

1
.menu {
2
font-size: 16px;
3
font-family: Roboto Slab, serif;
4
background-color: #C06574;
5
color: white;
6
position: fixed;
7
top: 0;
8
left: 0;
9
width: 100%;
10
padding: 10px 0;
11
z-index: 10;
12
}
13
.menu input[type="checkbox"] {
14
opacity: 0;
15
position: absolute;
16
left: -9999px;
17
}
18
.menu a, .menu .toggle-menu {
19
color: white;
20
text-decoration: none;
21
-webkit-transition: .3s all ease;
22
transition: .3s all ease;
23
}
24
.menu a:hover, .menu .toggle-menu:hover {
25
color: #E3DFE6;
26
}
27
.menu .toggle-menu {
28
display: none;
29
padding-left: 10px;
30
}
31
.menu .toggle-menu:hover {
32
cursor: pointer;
33
}
34
@media screen and (max-width: 2000px) {
35
.menu .toggle-menu {
36
display: block;
37
}
38
}
39
@media screen and (max-width: 2000px) {
40
.menu .nav {
41
background: #b95364;
42
position: fixed;
43
height: 100%;
44
width: 320px;
45
top: 41px;
46
left: 0;
47
-webkit-transition: .3s all ease;
48
transition: .3s all ease;
49
-webkit-transform: translateX(-320px);
50
transform: translateX(-320px);
51
overflow-y: auto;
52
font-size: 18px;
53
}
54
.menu #toggle-responsive:checked ~ .nav {
55
-webkit-transform: translateX(0);
56
transform: translateX(0);
57
}
58
}
59
.menu label.toggle {
60
color: transparent;
61
position: relative;
62
}
63
.menu label.toggle:hover {
64
cursor: pointer;
65
}
66
.menu label.toggle:hover:before {
67
width: 0;
68
height: 0;
69
border-style: solid;
70
border-color: transparent transparent transparent #E3DFE6;
71
border-width: 5.33333px;
72
}
73
.menu label.toggle:before {
74
content: "";
75
font-size: 16px;
76
-webkit-transition: .3s all ease;
77
transition: .3s all ease;
78
position: absolute;
79
top: 50%;
80
left: 5px;
81
-webkit-transform: translateY(0%) rotate(90deg);
82
transform: translateY(0%) rotate(90deg);
83
width: 0;
84
height: 0;
85
border-style: solid;
86
border-color: transparent transparent transparent white;
87
border-width: 5.33333px;
88
}
89
@media screen and (max-width: 2000px) {
90
.menu label.toggle:before {
91
left: 15px;
92
}
93
}
94
.menu ul {
95
position: relative;
96
}
97
.menu ul,
98
.menu ul li {
99
list-style: none;
100
padding: 0;
101
margin: 0;
102
}
103
.menu ul > li {
104
position: relative;
105
padding: 10px;
106
}
107
@media screen and (min-width: 2001px) {
108
.menu ul > li {
109
display: inline;
110
padding: 0 10px;
111
}
112
}
113
.menu ul > li > ul {
114
position: absolute;
115
top: calc(100% + 10px);
116
left: 0;
117
background: #C06574;
118
display: none;
119
}
120
@media screen and (max-width: 2000px) {
121
.menu ul > li > ul {
122
position: relative;
123
left: -10px;
124
background: rgba(0, 0, 0, 0.1);
125
width: calc(100% + 20px);
126
}
127
}
128
@media screen and (min-width: 2001px) {
129
.menu ul > li > ul > li {
130
display: block;
131
white-space: nowrap;
132
}
133
.menu ul > li > ul > li label.toggle:before {
134
-webkit-transform: translateY(-50%);
135
transform: translateY(-50%);
136
}
137
.menu ul > li > ul ul {
138
left: 100%;
139
top: 0;
140
}
141
}
142
.menu ul > li input[type="checkbox"]:checked + ul {
143
display: block;
144
}

See the Pen Checkbox-Hack Dropdown (responsive) by Myri ( @mynimi) on CodePen.

And that’s it.

So is this better than JavaScript?

So is this better than JavaScript?

This is the question. If you should actually go with a solution like this. Well, in case you have to solve this problem without JavaScript - Go with this. In Reallife, though, the javascript solution is the one I would go with. Reason for that being the fact, that it is written faster and all-over support is a little better. Still, I like trying to tackle some problems without the use of JavaScript, just to see how far you can come with just HTML and CSS.

Search

Results will appear as you type