How to Build an Accordion Component With the CSS Checkbox Hack

Read MoreWhat You’ll Be CreatingIn this short tutorial, we’ll learn how to build a CSS-only flexible accordion component by taking advantage of the “CSS checkbox hack technique”. Most importantly, our component will be fully responsive and its layout will switch between horizontal and vertical depending on the viewport size. Along the way, we’ll discuss how the CSS Checkbox Hack works, and look at some other Checkbox Hack inspiration from developers on CodePen. Sound interesting?Our Responsive CSS AccordionHere’s what we’ll be building during this tutorial:

Note: This tutorial assumes some flexbox knowledge. If you’re just beginning, check out this beginners flexbox tutorial:Flexbox
A Comprehensive Guide to Flexbox Alignment
Anna MonusWait, What’s the CSS Checkbox Hack?The CSS checkbox hack allows you to control certain styles depending on whether checkboxes (or radio buttons) are checked or not. It uses the :checked pseudo-class selector, which enables us to say “if a checkbox is checked, apply these style rules to its sibling etc.”Developers usually hide the input itself, controlling the checked value via its label, so users can’t even tell they’re toggling a checkbox at all.It’s a favourite of mine; in fact, I’ve used the same technique in several tutorials:CSS
Quick Tip: How to Create an Off-Canvas Feedback Form With Pure CSS
George Martsoukos
CSS Selectors
How to Build a Filtering Component in Pure CSS
George Martsoukos

1. Begin With the HTML MarkupFor the purposes of this exercise, we’ll grab some content from Wikipedia about four different things: animals, plants, space, and rivers.Then, we’ll create the corresponding radio buttons which we’ll group under the wiki keyword:<input type=”radio” id=”animal” name=”wiki” value=”Animal” checked>
<input type=”radio” id=”plant” name=”wiki” value=”Plant”>
<input type=”radio” id=”space” name=”wiki” value=”Space”>
<input type=”radio” id=”river” name=”wiki” value=”River”>Build an Unordered ListNext up, we’ll specify an unordered list with four list items. Each list item will represent an accordion item/pane and hold two elements: Firstly, a label which will serve as the accordion’s title and be responsible for opening the target item. Its for value should match the id value of one of the aforementioned radio buttons. 
Secondly, a div  element which will serve as the accordion’s content area.
By default, one pane in our accordion must be open. With that in mind, let’s add the checked attribute to the first radio button.Putting it all together, here’s the markup that we’ll need:<ul class=”accordion”>
<li>
<label for=”animal” class=”accordion-title”>
<span>…</span>
<span class=”accordion-heading”>…</span>
</label>

</li>
<li>
<label for=”plant” class=”accordion-title”>
<span>…</span>
<span class=”accordion-heading”>…</span>
</label>

</li>
<li>
<label for=”space” class=”accordion-title”>
<span>…</span>
<span class=”accordion-heading”>…</span>
</label>

</li>
<li>
<label for=”river” class=”accordion-title”>
<span>…</span>
<span class=”accordion-heading”>…</span>
</label>

</li>
</ul>
2. Define the StylesWith the markup ready (and inline with the CSS checkbox hack I described earlier) we’ll first visually hide the radio buttons by moving them off screen:input[type=”radio”] {
position: absolute;
left: -9999px;
}The accordion will have a maximum width, a minimum height, and behave as a flex container:/*CUSTOM VARIABLES HERE*/

.accordion {
display: flex;
width: calc(100% – 20px);
max-width: 800px;
min-height: 380px;
margin: 0 auto;
background: var(–accordion-color);
color: var(–white);
}Also, each list item will serve as a flex wrapper:.accordion li {
display: flex;
}The accordion items should be separated, so let’s give them a border:/*CUSTOM VARIABLES HERE*/

.accordion li:not(:last-child) {
border: 1px solid var(–separator-color);
}Each title (label) inside an item will be a flex container and its child elements will be distributed vertically across the main axis. In addition, all items will have a 70px width:/*CUSTOM VARIABLES HERE*/

.accordion .accordion-title {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 70px;
font-size: 1.4rem;
font-weight: bold;
line-height: normal;
padding: 20px 10px;
background: var(–title-color);
transition: color 0.1s;
}

.accordion .accordion-title:hover {
color: var(–active-color);
}The text inside the .accordion-heading will be rotated vertically:.accordion .accordion-heading {
display: inline-block;
white-space: nowrap;
transform-origin: bottom;
transform: rotate(-90deg) translate(50%, 50%);
}Initially, apart from the first pane, all the other panes will be hidden:.accordion .accordion-content {
display: none;
align-items: center;
padding: 20px;
}
3. Checkbox Hack: Toggle the PanesNow for the Magic. Each time we click on a label, its associated content should appear. To make that happen, we’ll take advantage of the :checked pseudo-class, the subsequent-sibling selector (~), and the adjacent sibling combinator (+). If you need a refresher of what these selectors do, check out this tutorial:CSS
Mastering General Sibling Selectors: Custom Tab Navigation
Gabrielle WeeSo, when an item becomes visible, it will receive display: flex and not display: block. This is because we want to vertically center its content by taking advantage of the align-items: center property value. Additionally, the colors of the active pane have to change, so a visitor can clearly understand which pane is open.Optionally, each time a radio button receives focus, we can add an outline to its associated label. This small detail will help us enhance the accessibility of our component.Here’s the related CSS stuff:/*CUSTOM VARIABLES HERE*/

[value=”Animal”]:checked ~ .accordion [for=”animal”] + .accordion-content,
[value=”Plant”]:checked ~ .accordion [for=”plant”] + .accordion-content,
[value=”Space”]:checked ~ .accordion [for=”space”] + .accordion-content,
[value=”River”]:checked ~ .accordion [for=”river”] + .accordion-content {
display: flex;
}

[value=”Animal”]:checked ~ .accordion [for=”animal”],
[value=”Plant”]:checked ~ .accordion [for=”plant”],
[value=”Space”]:checked ~ .accordion [for=”space”],
[value=”River”]:checked ~ .accordion [for=”river”] {
color: var(–active-color);
}

/*optional*/
[value=”Animal”]:focus ~ .accordion [for=”animal”],
[value=”Plant”]:focus ~ .accordion [for=”plant”],
[value=”Space”]:focus ~ .accordion [for=”space”],
[value=”River”]:focus ~ .accordion [for=”river”] {
outline: 1px solid var(–active-color);
}
4. Going ResponsiveAs we’ve already discussed, on small screens the items inside the accordion should be stacked, so the accordion will have a vertical layout. Thanks to flexbox, we can implement this design without putting in too much effort. In fact, all we have to do is to update the direction of the flex wrappers and reset the transform property value of the .accordion-heading.Have a look at the responsive styles within a media query below:@media screen and (max-width: 650px) {
.accordion {
min-height: 0;
}

.accordion,
.accordion li {
flex-direction: column;
}

.accordion .accordion-title {
flex-direction: row;
width: auto;
}

.accordion .accordion-heading {
transform: none;
}

.accordion .accordion-title,
.accordion .accordion-content {
padding: 20px;
}
}
5. Bonus: Limited ContentIn our case, there’s a lot of content inside each of the accordion items, so everything looks great. But, let’s ensure that the accordion will still work fine when there isn’t enough content within the panes (e.g. a pane contains only social links). To satisfy this scenario, we have to do two things:Add flex-grow: 1 to the list item which contains the active pane. To target only that item and not all of them, we’ll need to add a new custom attribute (data-radio) to the list items with value their label’s value.
Add flex-grow: 1 to the .accordion-content, so it will expand and cover the full parent width.
Center horizontally the content of the .accordion-content thanks to the justify-content: center.
With all the above in mind, we’ll modify our HTML as follows:<ul class=”accordion”>
<li data-radio=”animal”>…</li>
<li data-radio=”plant”>…</li>
<li data-radio=”space”>…</li>
<li data-radio=”river”>…</li>
</ul>Then, in the CSS we’ll include these styles:.accordion .accordion-content {
justify-content: center;
flex-grow: 1;
}

[value=”Animal”]:checked ~ .accordion [data-radio=”animal”],
[value=”Plant”]:checked ~ .accordion [data-radio=”plant”],
[value=”Space”]:checked ~ .accordion [data-radio=”space”],
[value=”River”]:checked ~ .accordion [data-radio=”river”] {
flex-grow: 1;
}
ConclusionThat’s it folks! In this quick tutorial, we managed to build a pure CSS accordion by taking advantage of the “CSS checkbox hack technique”. Hopefully, you enjoyed this exercise and you’ll spend some time extending it for your own specific purposes.Here’s a reminder of what we built:

Extra ProjectTo conclude, just keep in mind that with this implementation only one pane of the accordion can be open at a time. That’s because we used radio buttons in the markup. In case you want to display multiple accordion items at the same time, replace the radio buttons with checkboxes and make the necessary changes (hide the checkboxes) in the CSS.As always, thanks a lot for reading!Checkbox Hack Inspiration on CodePenWhere better to see what others have built with the CSS Checkbox Hack than CodePen? Here are some great examples to whet your appetite:

Responsive Emoji Toggles by George W. Park

CSS Only Tabbed Content by Stephen Greig

Modal pure CSS – “Checkbox Hack” by BeardedBear

CSS checkbox hack nav by JAD3

Further ReadingCheck out these resources to learn more about similar CSS techniques, and other ways of building accordion components (did someone say Bootstrap?!):CSS Selectors
How to Build a Filtering Component in Pure CSS
George Martsoukos
CSS
Quick Tip: How to Create an Off-Canvas Feedback Form With Pure CSS
George Martsoukos
Bootstrap 4
Quick Tip: How to Customize Bootstrap 4’s Accordion Component
George Martsoukos

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top

Hey Twin Tiers! Would you like a...

free website, free email, free SEO, & free EVERYTHING for a full YEAR?!?!?

Click below and be entered to win FREE digital media for a year!

A custom-built website, email addresses, SEO, and so much more… all for free… all for a full year!

Why? … Well, we’re new here and we’d like to get known. But HURRY… this definitely wont last!!!

We swear there is no catch… so what do you have to lose?