A simple collapsible menu for mobile devices using only CSS

03 Jan 2021 - tsp
Last update 03 Jan 2021
Reading time 7 mins

Introduction

Back in the old days of the world wide web it was common to write fixed layout pages for the desktop (remember the days of this page is optimized for 600x480 and Internet Explorer?). This turned out to be a really bad idea as was already known to professional web developers but since the web expanded this bad practice swamped the net. As mobile pages emerged this turned out to be a even worse idea than before and people started to write additional mobile pages to which they redirected the browsers based on browser sniffing or some fancy JavaScript based decisions - bad idea.

The WWW and also HTML is basically designed about some core principles:

The first two parts are addressed by HTML (and also partially CSS). The hypertext markup language has been designed to allow one to annotate raw text with semantic information - keep that in mind: HTML itself is not about design, it’s about semantics. It doesn’t matter if you want to see an emphasis in italic font or bold - it’s an <em>. There are only a few HTML tags that are primarily used for design purposes - <div> and <span> which provide non semantic ways for grouping. All other elements have semantic meaning that should be kept in mind.

To get back to the topic of this blog post: Since HTML is totally device agnostic there is no reason to write separate pages for different media or devices. This inherent and basic property of HTML that existed from day one on is of today called responsive design and it’s achieved by either choosing sane CSS layout rules or overriding them in some corner cases using media queries. Media queries allow one to apply stylesheet rules only on a given subset of output devices or under given conditions like a specific width (in scalable units). These are used in CSS following the @ sign (as an example see below). These rules should be used as seldom as possible - but for some applications where one really wants different behavior they’re a really nice method to adjust the page depending on device properties - like for this collapsible menu on mobile devices.

Why a collapsible menu? Basically the menu bar is usually shown on top of a page. As the page width gets lower and lower the navigation has to reflow into a longer and longer vertical list - and users have to scroll over the navigation every time. It’s a good idea though that users directly see the main content as fast as possible - so the idea is to collapse the navigation as long as the user doesn’t require it. This also fits the behavior people know from their mobile applications anyways. In the early days (before CSS sibling selectors) this has usually been done using JavaScript which is a bad idea for any essential stuff.

Keep in mind that you’re not selecting a specific device though - just resize your browser window on your computer if you’re reading this page on a desktop machine and you’ll see the same effect …

CSS selectors

Just as a short recapitulation since they’re not known to the older guys who aren’t as fit with CSS3 - the following combiners are supported in modern CSS:

In addition to these selectors (and the optional usage of attribute selectors) the :checked pseudo selector is used.

Implementation

The basic idea is simple: Add a hidden checkbox, an label assigned to that checkbox (any label that’s assigned to an control triggers it’s clicked even when being clicked / touched) and use the checked pseudo-class of the checkbox to display an sibling div or ul / ol element. Using lists also allows a reflowing menu in non-mobile mode (like done on this page for example)

First one has to build the menu. The most important part is that the part of the menu that will be collapsed and shown/hidden is a sibling of the checkbox, not a child or ancestor. For easier styling I’ll add the whole navigation into a separate division:

<div class="menuToggleBox">
	<input type="checkbox" id="mainMenuToggle">
	<label for="mainMenuToggle"> Menu </label>

	<ul>
		<li> <a href="/">          Home       </a> </li>
		<li> <a href="/example01"> Example 01 </a> </li>
		<li> <a href="/example02"> Example 02 </a> </li>
		<li> <a href="/example03"> Example 03 </a> </li>
		<li> <a href="/example04"> Example 04 </a> </li>
	</ul>
	<div class="clearfloat"></div>
</div>

Now one can design to go either the mobile-first or the desktop-first route. Since even Google recommends this one should - if one hasn’t any indications from user statistics that desktop users are the majority - take the mobile first approach.

Generally we want to:

On mobile we want to:

/* General */

#mainMenuToggle {
	display: none;
}

div.clearfloat {
	clear: both;
}

/* Mobile */

div.menuToggleBox label {
	display: block;
	padding-left: 2.3em;
	font-size: 150%;
	background: url(/assets/images/png/hamburger.png) no-repeat left center;
}

#mainMenuToggle~ul {
	display: none;
}

#mainMenuToggle:checked~ul {
	display: block;
}

#mainMenuToggle:checked~label {
	background: url(/assets/images/png/closemenu.png) no-repeat left center;
}

div.menuToggleBox ul li {
	float: none;
	padding: 1ex 1em 1ex 1em;
}

For desktop I’ll use an inverse media query that uses the inverse properties of the targeted mobile solution:

@media not screen or (min-width: 62em) {
	div.menuToggleBox label {
		display: none;
	}

	div.menuToggleBox ul {
		display: block;
		padding: 0 0 0 0;
		margin: 0 0 0 0;
	}
	div.menuToggleBox ul li {
		display: block;
		float: left;
		border-right: 1px solid #FFFFFF;
		border-top: 1px solid #FFFFFF;
		font-family: monospace;
		font-size: 130%;
	}
	div.menuToggleBox ul li a, div.menuToggleBox ul li a:visited {
		color: light-blue;

		padding-left: 0.5em;
		padding-right: 0.5em;
		padding-top: 0.5ex;
		padding-bottom: 0.5ex;
	}
	div.menuToggleBox ul li a:hover {
		color: light-red;
		font-weight: bold;

		padding-left: 0.2em;
		padding-right: 0.2em;
		padding-top: 0.2ex;
		padding-bottom: 0.2ex;
	}
}

And since a friend asked how one can implement the transition effects - that’s also rather easy:

div.menuToggleBox ul li a, div.menuToggleBox ul li a:visited {
	transition: font-weight 1s,
	   background-color 1s,
	   color 1s,
	   padding-left 1s,
	   padding-right 1s,
	   padding-top 1s,
	   padding-bottom 1s;
}

This article is tagged:


Data protection policy

Dipl.-Ing. Thomas Spielauer, Wien (webcomplains389t48957@tspi.at)

This webpage is also available via TOR at http://rh6v563nt2dnxd5h2vhhqkudmyvjaevgiv77c62xflas52d5omtkxuid.onion/

Valid HTML 4.01 Strict Powered by FreeBSD IPv6 support