James TW

Sep 13, 2016

4 min read

3 Useful Things You Can Do With Only CSS Using the <label> Hack

How to create modals, accordions, and tab groups without Javascript.

Normally, the <label> HTML element exists to give users an easier target to click when they want to focus on an <input>. However, this magic link between the two elements can be hacked to build components like tabs, modals, and accordions using only CSS.

A quick refresher on how <label> works: Each <input> (or <select>, or <textarea>, etc.) can have an id attribute. If it does, any <label> can have its for attribute set to that ID. When the two elements are linked in this way, clicking on the label simulates a click on the input element.

<label for="box-1">Check the Box</label>
<input type="checkbox" id="box-1">

Labels can be styled however you want, of course. With a bit of effort, we can make them look like a button, for example:

I used a checkbox in my example because it’s a very useful element. Clicking on the label effectively toggles the checked state of the checkbox. CSS has a :checked selector which can apply styles only when a checkbox is checked.

#myCheckbox:checked {
/* Checked styles */

The final ingredient is the under-appreciated sibling selector: ~. The sibling selector allows us to apply styles to siblings of a given element. We can combine this with the :checked selector to identify when a checkbox is checked and change the style of all sibling elements. For example, we can reveal a previously hidden box when the checkbox element is :checked.

One caveat about sibling selectors (including the adjacent sibling selector, +). The elements following the sibling selector must come after the initial element in the DOM. In the above example, the <img> element must follow the <input> element in the DOM. Read more on sibling selectors.

Now all we need to do is hide the actual checkbox, since it’s just needed for the :checked state.

CSS-only Modals

Let’s make a pop-up modal using what we’ve just learned. It’s already pretty close, if you think about it. A button reveals a piece of content. There’s no reason that content can’t be styled like a modal and overlap the whole screen.

I’ll be jumping into Sass in the following examples, making heavy use of the & selector. Brush up on your ampersand if you get lost. I’m also naming my classes according to the BEM naming convention.

This is a pretty big leap from the previous example, so I’ll explain. I put all modal-related stuff in its own container, which holds the hidden checkbox, the modal itself, and the dark mask which covers the rest of the screen. When the checkbox isn’t checked, the modal and the mask are hidden. When it is checked, they are revealed. All the rest of the styling is pretty standard for modals.

One cool feature we just added was the ability to click on the mask to close the modal again. Notice that it’s also a label with the for property set to toggle the checkbox off again. You can have multiple labels linked to the same checkbox, and that’s really useful in making CSS modals.

We can get more complex with the styling and modal contents, but those are the basics. Here are two more Codepens I created with extra features if you want to add animations or close buttons inside the modal body:

CSS-only Accordions

By now, you can probably guess how an accordion built using the checkbox label hack might work. Accordions have a collapsed state and an expanded state. When it’s collapsed, you’ll see a header (which is actually a <label>) which you can click on to open the accordion body. You can then click the header again to close it.

Ultimately, this is very similar, if not simpler than the modal example above. Accordions have simpler styles than modals, and don’t usually require any extra close buttons. I included a ::before element as an indicator of whether the accordion is expanded or collapsed.

Sometimes, if there are multiple accordions next to one another, you might want to only allow one to be open at a time. In this case, we can use radio buttons instead of checkboxes. As long as all the <input> elements have the same name attribute, only one will ever be open.

Unfortunately, once an accordion is opened using this method, you can never close all of them again. It’s only just CSS, after all.

CSS Only Tabs

Finally, we can use what we know about using radio buttons to make a group of tabs. When we click a label, the associated tab will display in the content area, and when we click another label, a different tab will display.

The label hack isn’t quite as flexible in this example. Because the content is overlapping, we use position: absolute on the content areas and therefore must manually set the height of the tab group. If the content is larger than the fixed height, it will add scrollbars, but it won’t automatically increase the height of the content area.

While this example works, it has its limitations, as do the modal and accordion strategies. It’s not always appropriate to replace robust Javascript components with CSS-only alternatives just because you can. However, when users don’t have Javascript enabled, this strategy provides graceful degradation.

Modern CSS is pretty powerful, even if it can’t always replace Javascript. These examples make great use of sibling selectors and state selectors like :checked to provide a little bit of interactive logic to our components without Javascript.

Love podcasts or audiobooks? Learn on the go with our new app.