Tutorial

Implementing A Pure CSS Collapsible

Published on May 2, 2020
authorauthor

Alligator.io and Andy Hattemer

Implementing A Pure CSS Collapsible

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.

Here’s what our collapsible looks like:

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.

Styling Our Collapsible

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.

A Note on Accessibility

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.

All Styles

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.

Learn more about our products

About the authors
Default avatar
Alligator.io

author


Default avatar

Community Builder

Then: Learned to build the internet on DigitalOcean Community. Now: Building DigitalOcean Community on the internet.


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.

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
6 Comments


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:

.wrap-collabsible {
  margin-bottom: 1.2rem 0;
}

Error in parsing value for ‘margin-bottom’. Declaration dropped.

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:

  • <summary> <details> -method
  • :target selector -method

?

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.

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Become a contributor for community

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

DigitalOcean Documentation

Full documentation for every DigitalOcean product.

Resources for startups and SMBs

The Wave has everything you need to know about building a business, from raising funding to marketing your product.

Get our newsletter

Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

New accounts only. By submitting your email you agree to our Privacy Policy

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Get started for free

Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

*This promotional offer applies to new accounts only.