Collapsible widgets are a popular way to create sections of content that can contract and expand. There are a ton of different implementations out there. Here, thanks to checkbox input elements, label elements and the :checked pseudo-selector, we’ll be able create such widget without the need for extra JavaScript.
See the Pen KKzWqVX by alligatorio (@alligatorio) on CodePen.
And here’s the HTML markup for it:
<div class="wrap-collabsible">
<input id="collapsible" class="toggle" type="checkbox">
<label for="collapsible" class="lbl-toggle">More Info</label>
<div class="collapsible-content">
<div class="content-inner">
<p>
QUnit is by calling one of the object that are embedded in JavaScript, and faster JavaScript program could also used with
its elegant, well documented, and functional programming using JS, HTML pages Modernizr is a popular browsers without
plug-ins. Test-Driven Development.
</p>
</div>
</div>
</div>
If you want a collapsible to be opened by default, simply set the checked attribute on the checkbox:
<input id="collapsible2" class="toggle" type="checkbox" checked>
See the Pen qBZrjqG by alligatorio (@alligatorio) on CodePen.
Note that each label should be associated with the correct checkbox, so each checkbox element needs a unique id and each label’s for attribute should point to the corresponding checkbox’s id.
Let’s breakdown the styles bit by bit…
First we set the checkbox element to display: none
. The checkbox will be invisible and its label will be used instead to check or uncheck it. Later, you’ll see that we’ll use the CSS :checked pseudo-selector to style things differently when the hidden checkbox is checked:
input[type='checkbox'] {
display: none;
}
Next, we style our default label. Here nothing really special is going on, except for the fact that we make our label display as a block element with display: block
:
.lbl-toggle {
display: block;
font-weight: bold;
font-family: monospace;
font-size: 1.2rem;
text-transform: uppercase;
text-align: center;
padding: 1rem;
color: #A77B0E;
background: #FAE042;
cursor: pointer;
border-radius: 7px;
transition: all 0.25s ease-out;
}
.lbl-toggle:hover {
color: #7C5A0B;
}
For the small arrow, some clever use of borders makes it easy to create the triangle shape:
.lbl-toggle::before {
content: ' ';
display: inline-block;
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
border-left: 5px solid currentColor;
vertical-align: middle;
margin-right: .7rem;
transform: translateY(-2px);
transition: transform .2s ease-out;
}
Refer to this post from CSS-Tricks for all your CSS triangle needs.
You may have noticed also that we made use of the currentColor built-in variable so that our triangle is the same color as our label’s text.
Let’s also give some basic styles to the inner content:
.collapsible-content .content-inner {
background: rgba(250, 224, 66, .2);
border-bottom: 1px solid rgba(250, 224, 66, .45);
border-bottom-left-radius: 7px;
border-bottom-right-radius: 7px;
padding: .5rem 1rem;
}
Now we can start with the interesting part. By default, the collapsible-content div will have a max-height value of 0px, making it completely hidden:
.collapsible-content {
max-height: 0px;
overflow: hidden;
transition: max-height .25s ease-in-out;
}
When a collapsible’s checkbox gets checked behind the scenes by clicking its label, we’ll set the content div to a high-enough max-height value so that it can grow to display all its internal content.
Instead of trying to figure out a good height manually, you can also just make use of viewport units with something like 100vh. Using something like 100% would also work, but you’d lose the ability to use transitions. Note that you’ll still probably want to using something like 100% if you think the content in the collapsible could be taller than the viewport.
We make use of the adjacent sibling selector (+) to select our content div when the checkbox is checked:
.toggle:checked + .lbl-toggle + .collapsible-content {
max-height: 100vh;
}
We use max-height instead of height because we want to avoid using a hard-coded height and want to be able to place content of arbitrary height into our collapsibles.
And now we do something really similar using the adjacent sibling selector to rotate our little triangle shape when the collapsible is expanded and to adjust the bottom right and border left radius of our label:
.toggle:checked + .lbl-toggle::before {
transform: rotate(90deg) translateX(-3px);
}
.toggle:checked + .lbl-toggle {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
And there you have it! A pretty straightforward way to create a collapsible section without using any scripting at all.
As it is now, our collapsible widget is not really accessible. We’ll have to add a touch of JavaScript to make it accessible. I know, I know, the whole thing is supposed to be implemented using zero JavaScript. What can I say!
Accessibility technology is constantly improving, leave a comment below if there is a better way to make this accessible.
In the following script we select all the toggle labels and listen for keydown events. If the pressed key is either the enter or the spacebar keys, we trigger a click on the label.
let myLabels = document.querySelectorAll('.lbl-toggle');
Array.from(myLabels).forEach(label => {
label.addEventListener('keydown', e => {
// 32 === spacebar
// 13 === enter
if (e.which === 32 || e.which === 13) {
e.preventDefault();
label.click();
};
});
});
To make the label focusable, we add tabindex="0"
to it:
<label for="collapsible3" class="lbl-toggle" tabindex="0">With A11y</label>
See the Pen OJNpgpb by alligatorio (@alligatorio) on CodePen.
Here are the full set styles at once, for your reference:
.wrap-collabsible {
margin-bottom: 1.2rem 0;
}
input[type='checkbox'] {
display: none;
}
.lbl-toggle {
display: block;
font-weight: bold;
font-family: monospace;
font-size: 1.2rem;
text-transform: uppercase;
text-align: center;
padding: 1rem;
color: #A77B0E;
background: #FAE042;
cursor: pointer;
border-radius: 7px;
transition: all 0.25s ease-out;
}
.lbl-toggle:hover {
color: #7C5A0B;
}
.lbl-toggle::before {
content: ' ';
display: inline-block;
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
border-left: 5px solid currentColor;
vertical-align: middle;
margin-right: .7rem;
transform: translateY(-2px);
transition: transform .2s ease-out;
}
.toggle:checked + .lbl-toggle::before {
transform: rotate(90deg) translateX(-3px);
}
.collapsible-content {
max-height: 0px;
overflow: hidden;
transition: max-height .25s ease-in-out;
}
.toggle:checked + .lbl-toggle + .collapsible-content {
max-height: 100vh;
}
.toggle:checked + .lbl-toggle {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.collapsible-content .content-inner {
background: rgba(250, 224, 66, .2);
border-bottom: 1px solid rgba(250, 224, 66, .45);
border-bottom-left-radius: 7px;
border-bottom-right-radius: 7px;
padding: .5rem 1rem;
}
🌈 Enjoy your fancy almost-CSS-only collapsible!
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
In Firefox 116.0.3 I get a warning due to this rule:
Apart from that, this works great. I’ll go with
<details>
though, as mentioned by Harry Cutts.I currently have this implemented on an intranet, and it works just fine. I’m able to use multiple at once but I’m wondering, is it possible when toggling one open to auto close it when toggling another?
Just installed on my website, looking good after customizing it, but I need help with something.
I put content in it, an image(535x315) and a YouTube video(585x315) both contents are side by side while must resolutions works perfectly well, I found issues with resolutions between 700 and 1268, how can I make those resolutions could work properly with the content that I put inside.
Wrap-collapsible at 1080p https://i.imgur.com/cIOecNB.png
Wrap-collapsible at 1268 https://i.imgur.com/Cr7wAP5.png
Thanks in advance.
Is it somehow possible to use multiple of these at once? Currently when using more than one, clicking on any of them just opens the first one
Thank you for this tutorial.
Would you mind clarifying what you mean by making it ‘Accessible’? My intention is to use this without javascript and when I try this collapsible method on my website, without javascript, it works as intended. Am I using it wrong?
Also, what’s the browser compatibility with this method compared to:
?
Thanks again and hope to hear from you soon.
Another option is the
<details>
element, which I assume is accessible by default since it’s a browser built-in.