CSSSep 14, 2013 · 3 min read

Accordions without JavaScript: Storing Application State with HTML, CSS

What if you could build an accordion without writing a single line of JavaScript? Using a hidden checkbox and the CSS ~ sibling selector, you can capture toggle state entirely in HTML and CSS.

Here is a little thought experiment revolving around the ~ selector in CSS. When used in conjunction with a hidden input, it can effectively store the state of an application without using JavaScript.

Warning: This is intended to be an experiment, and not recommended practice. Semantics completely go out the window, and some of the effects rely on CSS3.

The Traditional JS Accordion

The traditional method of implementing an accordion looks something like this:
HTML
Section 1
Content for section 1
JavaScript
$('h1').on('click', function() {
  $(this).next('section').slideDown();
});

We can implement this functionality without using JavaScript if we can wrangle HTML and CSS to do the following:

  • Capture the "state" of the accordion
  • Create a transition animation

Capturing State

I started by asking: How can we capture user interaction and render a change in "state" without using JavaScript?

A common UI pattern is to have a toggle: the user clicks on an input, and something's state changes; click again, and the state is reverted. Often well bind an onClick event to change the UI.

We can emulate this behavior by taking advantage of the following HTML and CSS rules:

  • You can interact with a form element such as a checkbox by clicking on a label, provided they have a proper for/I'd relationship
  • The + and ~ selectors allow you to add styles to "nearby" elements

Consider the following: http://jsfiddle.net/tuanderful/nSV4r/

This is implemented with a hidden checkbox and a label that controls it.

HTML


    On
    Off

We hide the input, and label becomes our trigger. Then we use the sibling selectors to change CSS when the input is checked.

CSS
input {
    display: none;
}

/* default span style */
label span {
    display: none
}
input ~ label .offState {
    display: inline;
}

/* styling for "on" state */
input:checked ~ label .onState {
    display: inline;
}
input:checked ~ label .offState {
    display: none;
}

HTML/CSS Accordions

We can expand upon those two principles to build an accordion using just HTML and CSS. The markup would look something like this:
HTML

  
  
    Section 1
  
  Content for Section 1

And the CSS:

CSS
section, input {
    display: none;
}
input:checked ~ section {
    display: block;
}

You can see this in action here: http://jsfiddle.net/tuanderful/nSV4r/2/

Here, we're using the ~ selector to give us more flexibility, so that the section doesn't necessarily need to be an adjacent sibling. Again, it follows the same principles as before: use the label as a target to drive the state (:checked, :unchecked) of the input, and only display the section if the corresponding checkbox is checked.

Adding Flair

Here's a little bit of CSS transitions to polish the interaction: http://jsfiddle.net/tuanderful/nSV4r/2/

Note that since you are sliding up and down, there are two things to remember:

  • You cannot have a transition if you toggle between display: none and display: block
  • You cannot transition on the height property; you have to use max-height instead.