Welcome to a tutorial on how to create a simple responsive accordion. Yes, there are a lot of “accordion plugins” in the world, but some of them require a third-party framework. It simply does not make sense to load an entire library for a single accordion – So here it is, a super lightweight accordion using only pure CSS. Read on!
ⓘ I have included a zip file with all the source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.
QUICK SLIDES
TABLE OF CONTENTS
DOWNLOAD & NOTES
Firstly, here is the download link to the example code as promised.
QUICK NOTES
If you spot a bug, feel free to comment below. I try to answer short questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.
EXAMPLE CODE DOWNLOAD
Click here to download all the source code in a zip file – I have released it under the MIT License, so feel free to build on top of it if you want to.
CSS ACCORDION
All right, let us now get into creating an accordion using pure HTML and CSS.
1) CHECKBOX ACCORDION
1A) THE DEMO
1B) THE HTML
<!-- FIRST TAB -->
<div class="tab">
<input id="tab-1" type="checkbox">
<label for="tab-1">Tab 1</label>
<div class="content"><p>Next to the risk dictates a nurse.</p></div>
</div>
<!-- SECOND TAB -->
<div class="tab">
<input id="tab-2" type="checkbox">
<label for="tab-2">Tab 2</label>
<div class="content"><p>Should the pace attack?</p></div>
</div>
<!-- THIRD TAB -->
<div class="tab">
<input id="tab-3" type="checkbox">
<label for="tab-3">Tab 3</label>
<div class="content"><p>A circumstance strikes a deserved trap.</p></div>
</div>
The HTML should be straightforward enough.
- Create a
<div class="tab">
container. - Sandwich 3 things inside the container.
<input id="tab-N" type="checkbox">
Required to toggle show/hide the tab contents.<label for="tab-N">
The tab label itself.<div class="content">
The tab contents.
- That’s all. Create as many
<div class="tab">
sections as required.
1C) THE CSS
/* (A) TABS CONTAINER */
.tab, .tab * {
font-family: arial, sans-serif;
box-sizing: border-box;
}
.tab { max-width: 600px; }
/* (B) HIDE CHECKBOX */
.tab input { display: none; }
/* (C) TAB LABEL */
.tab label {
/* (C1) DIMENSIONS */
position: relative; /* required for (f2) position:absolute */
display: block;
width: 100%;
margin-top: 10px;
padding: 10px;
/* (C2) COSMETICS */
font-weight: 700;
color: #fff;
background: #2d5faf;
cursor: pointer;
}
/* (D) TAB CONTENT - HIDDEN BY DEFAULT */
/* css animation will not work with auto height */
/* this is why we use max-height instead */
.tab .content {
background: #ccdef9;
overflow: hidden;
transition: max-height 0.3s;
max-height: 0;
}
.tab .content p { padding: 10px; }
/* (E) OPEN TAB ON CHECKED */
.tab input:checked ~ .content { max-height: 100vh; }
/* (F) EXTRA - ADD ARROW INDICATOR */
.tab label::after {
/* (F1) RIGHT ARROW */
display: block;
content: "\25b6";
/* (F2) PLACE AT RIGHT SIDE */
position: absolute;
right: 10px; top: 10px;
/* (F3) ANIMATED ARROW */
transition: all 0.4s;
}
/* (F4) ROTATE ARROW ON CHECKED */
.tab input:checked ~ label::after { transform: rotate(90deg); }
This may look complicated, but the essential mechanics are:
- (B) First, hide the ugly checkbox with
.tab input { display: none; }
. The checkbox will still function when we click on the label. - (D) Hide the tab contents by default –
.tab .content { max-height: 0 }
- (E) Show the tab contents when the checkbox is checked (when we click on the label) –
.tab input:checked ~ .content { max-height: 100vh; }
.
Yep, that’s all. The rest is pretty much just cosmetics.
2) RADIO BUTTON ACCORDION
2A) THE DEMO
2B) THE HTML
<!-- FIRST TAB -->
<div class="tab">
<input id="tab-1r" type="radio" name="tabr">
<label for="tab-1r">Tab 1</label>
<div class="content"><p>Next to the risk dictates a nurse.</p></div>
</div>
<!-- SECOND TAB -->
<div class="tab">
<input id="tab-2r" type="radio" name="tabr">
<label for="tab-2r">Tab 2</label>
<div class="content"><p>Should the pace attack? </p></div>
</div>
<!-- THIRD TAB -->
<div class="tab">
<input id="tab-3r" type="radio" name="tabr">
<label for="tab-3r">Tab 3</label>
<div class="content"><p>A circumstance strikes a deserved trap.</p></div>
</div>
Look no further, the only difference here is that we are using radio buttons and not checkboxes – This will enforce “only one tab can be opened at a time” for those who need it.
USEFUL BITS
That’s all for this guide, and here is a small section on some extras that may be useful to you.
OPENING TABS “BY DEFAULT”
How do we set a tab to be opened on page load? Very simply set the checked attribute – <input type="checkbox" checked>
.
RADIO ACCORDION RESTRICTION
Well, as some may have already noticed. The default behavior in HTML is that one radio button will be selected at all times, and there is no way we can unselect without using Javascript. So yes, the “restriction” in the radio button accordion is that one tab will be opened at all times.
LINKS & REFERENCES
- Javascript Accordion – Code Boxx
- Example on CodePen – Pure CSS Accordion
YOUTUBE TUTORIAL
INFOGRAPHIC CHEAT SHEET

THE END
Thank you for reading, and We have come to the end of this guide. I hope that it has helped you to create a better website, and if you have anything to share with this guide, please feel free to comment below. Good luck and happy coding!
Thanks a lot for this. Saves so much time by just using radio or checkbox! You are a genius.
It might be a very stupid question but since i can´t code at all i´ll go ahead and ask.
How can I use this on wordpress? I thought about adding the CSS in additional css in the customizer and add the tab class to a paragraph in gutenberg, but I´m pretty sure that´s not going to work because there would be no connection between the title and the content and between the titles and content and the other titles and content… In other words I don´t know where to put the html on wordpress
https://wpshout.com/quick-guides/wordpress-gutenberg-html/
Hi W.S.,
Thank you for the code/tutorial! If I wanted to move the arrow pointer to the left, how do I account for the heading text?
I changed the (F) section “right: 10px” to “left:10px”, which got the arrow on the correct side I want it to show on, but I don’t know how to fix the overlap it has with my tab title text.
Thank you!
Sylvia
Just offset the label
.tab label { padding-left: 50px; }
Thanks again for this. It is great. I have noticed an odd quirk perhaps. For the radio – one at a time drop-downs there seems to be an issue with scrolling when viewing on small screens so that sometimes when you click on a tab, the browser scrolls too far and you have to manually scroll back up to read the contents of the tab. Here is what I did to replicate which might explain better:
1) Include lots of paragraphs of text in each drop-down.
2) Include a few paragraphs of text after the drop-down (that is, paragraphs under the drop-down)
3) Click Tab 1. It displays fine.
4) Scroll down to Tab 2 and click Tab 2. It displays fine.
5) Scroll down to Tab 3 and click Tab 3. The browser scrolls down too far and you have to scroll back up to view the start of the text in Tab 3.
This only occurs on small sized screens. This behaviour does not occur on larger screens. I am not sure what causes it. Maybe there is a limitation on the amount of content you can include in a tab maybe?
Well, you kind of already answered your own question. That is a browser behavior and that is just the way how it renders the HTML/CSS… More CSS probably won’t fix anything. I will limit the content, or use Javascript to listen to the
animationend
event, then scroll back up automatically.Okay, thanks very much again.
Thanks so much for this.
One thing, though. I found that the absolute positioning of the label icon produced undesirable results. The icon – I chose plus and minus for this – was positioned to the edge of the window.
I fixed this by making the label element a flexbox.
Thanks again.
Thank you for this tutorial! You saved me a lot of time and headache!! So many accordions require JS, and I’m excited to have found one that doesn’t.
This is fantastic. I was using a javscript version, but this is working better. One quick question, for the radio button options, if I click Tab 1 to expand, is there any way I can click Tab 1 again to collapse it?
Sadly, no. There is no way we can uncheck an HTML radio button without using Javascript.
Nice! I’ve been looking for an accordion solution that doesn’t include Javascript which clean and simple. Is there an way to set, by default, one of the tabs to be open? Thanks in advance!
Jake
Very easy. Just check the checkbox –
<input type="checkbox" checked/>
Thanks so much for this! You saved my life (work-wise). So cool!
You are a genius!
Franc