CSS toggle menu

Many site owners want to let users toggle different sections of a web page. Often this is both unnecessary and annoying for the site visitors, but it can sometimes be useful for hiding otherwise distracting content.

This toggle menu is made with HTML and CSS only, no Javascript. I also have a Javascript toggle menu which I much prefer over this one. Using only CSS for a menu like this is more of a curiosity than a real advantage in my opinion.

Browser support

Tested to work in IE9 (but keyboard navigation seems a bit quirky) and other current (2015) browsers.

In older versions of Webkit-based browsers (like Safari 5, Chrome 12 and Android 4) the toggle sections can't be opened due to the Webkit sibling selector bug. According to Can I Use, usage of these browser versions is negligible now.


Click the top row buttons to Show all, Hide all or Toggle individual (default). When Toggle individual sections is selected, the buttons in the left column can be clicked to toggle individual sections. I admit the menu isn't very intuitive to use.

Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna...
...aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.

How it works

LABEL elements in the top row check hidden radio buttons, while LABEL elements in the left column check hidden checkboxes. The various :checked combinations change both the styling of the LABEL elements and the display of the associated DIV elements with CLASS "toggle".

Note that even though the labels for the checkboxes appear to be disabled in the Show all and Hide all states, the user can still toggle the checkboxes in the background by clicking the checkbox LABEL elements.

Keyboard navigation

If the radio buttons and checkboxes are hidden, keyboard navigation will be confusing, since the user can't know that the hidden radio buttons at the top must be navigated with the keyboard's arrow keys, while the individual sections are navigated with the Tab key and toggled with the space bar.

If you still want to hide the checkboxes and radio buttons, use CSS opacity: 0;. If display: none; or other hiding methods are used the hidden form elements can't be focused with keyboard tabbing. In this demo I also use position: absolute; (without offsets) to prevent the form elements from affecting the normal content flow.

Feature detection with the negation pseudo-class

In browsers that don't support the :checked pseudo-class the toggle content would normally be inaccessible. To prevent that I put :checked inside a :not negation pseudo-class for feature detection. This way the toggle content is only hidden if the browser supports :not and the toggle checkbox is not checked:

input[type=checkbox]:not(:checked) ~ .toggle {display: none;}

I use a similar trick for hiding the radio button labels in non-supporting browsers, where they would be unusable anyway. First the labels are hidden by default, then made visible again in browsers that support :not and :checked.

To hide the INPUT elements from non-supporting browsers I use :not(foo). Note that you can't use :not(:checked) for the INPUT elements, since that's also a selector for content in supporting browsers when a form element is not checked.

.toggle_container input {display: none;}
.toggle_container input:not(foo) {display: inline;}

Alas this feature detection doesn't help against the Webkit sibling selector bug described under the Browser support heading.

published: Dec 11, 2015
last modified: Jan 26, 2017