Accordions are great space-saving UI components that allow users to show and hide content sections without cluttering the page. While many developers reach for JavaScript to create accordions, you can build them using only CSS.
In this article we'll be creating an accordion component relying solely on CSS using a few clever tricks.
What We're Building
We'll create a simple accordion that:
- Expands/collapses when clicked
- Shows a visual indicator for the open/closed state
- Works without any JavaScript
- Is keyboard accessible
The final output will look like the following:
This accordion works by using hidden checkboxes and the CSS sibling selector (~).
When you click on a label, it toggles the associated checkbox. We then use CSS to detect the checkbox's state and show or hide the content accordingly.
No JavaScript is needed for this functionality!
The HTML Structure
First, let's set up our HTML:
<div class="accordion">
<!-- First Accordion Item -->
<div class="accordion-item">
<input type="checkbox" id="accordion-1" class="accordion-toggle">
<label for="accordion-1" class="accordion-title">
What is CSS?
<span class="accordion-icon"></span>
</label>
<div class="accordion-content">
<p>CSS (Cascading Style Sheets) is the language used to style web pages.
It describes how HTML elements should be displayed.</p>
</div>
</div>
<!-- Second Accordion Item -->
<div class="accordion-item">
<input type="checkbox" id="accordion-2" class="accordion-toggle">
<label for="accordion-2" class="accordion-title">
How does CSS work?
<span class="accordion-icon"></span>
</label>
<div class="accordion-content">
<p>CSS works by selecting HTML elements and applying styles to them.
The browser combines the content and styling to render the page.</p>
</div>
</div>
</div>
The key to our solution is the hidden checkbox (input type="checkbox")
combined with a label.
When a user clicks the label, it toggles the checkbox, which we'll use to control the accordion's open/closed state.
The CSS Magic
/* Basic styling for the accordion container */
.accordion {
width: 100%;
max-width: 600px;
margin: 0 auto;
border-radius: 8px;
overflow: hidden;
}
/* Style for each accordion item */
.accordion-item {
border-bottom: 1px solid #e0e0e0;
}
/* Hide the checkbox visually but keep it accessible */
.accordion-toggle {
position: absolute;
opacity: 0;
z-index: -1;
}
/* Style the accordion title/header */
.accordion-title {
display: flex;
justify-content: space-between;
padding: 16px;
background: #f7f7f7;
cursor: pointer;
font-weight: 500;
}
/* Create the plus/minus icon */
.accordion-icon {
position: relative;
width: 14px;
height: 14px;
}
.accordion-icon::before,
.accordion-icon::after {
content: '';
position: absolute;
background-color: #333;
transition: transform 0.25s ease-out;
}
/* Horizontal line */
.accordion-icon::before {
top: 6px;
left: 0;
width: 14px;
height: 2px;
}
/* Vertical line (becomes hidden when expanded) */
.accordion-icon::after {
top: 0;
left: 6px;
width: 2px;
height: 14px;
}
/* Style for accordion content */
.accordion-content {
max-height: 0;
padding: 0 16px;
overflow: hidden;
background: white;
transition: max-height 0.25s ease-in-out, padding 0.25s ease;
}
/* When checkbox is checked, expand the content */
.accordion-toggle:checked ~ .accordion-content {
max-height: 500px; /* Adjust based on your content needs */
padding: 16px;
}
/* Rotate the plus into a minus when open */
.accordion-toggle:checked ~ .accordion-title .accordion-icon::after {
transform: rotate(90deg); /* Makes vertical line disappear */
}
/* Accessibility: Focus styles */
.accordion-toggle:focus ~ .accordion-title {
outline: 2px solid #4a90e2;
}
How It Works
Let's break down the key CSS techniques used:
Hidden Checkbox: We use opacity: 0 and position: absolute to hide the checkbox visually while keeping it accessible to screen readers and keyboard navigation.
The Adjacent Sibling Selector (~): This is crucial for our CSS-only solution. The tilde selector targets sibling elements that follow the checkbox. When the checkbox state changes (by clicking the label), we can style elements that come after it.
max-height Transition: We set max-height: 0 for the collapsed content and expand it when the checkbox is checked. Using max-height instead of height allows us to accommodate content of varying lengths.
Plus/Minus Icon: The icon is created using pseudo-elements (::before and ::after). We create perpendicular lines and rotate one of them when the accordion is open to create a minus sign.
Focus Styles: We include styles for keyboard focus, ensuring our accordion is accessible to users navigating with a keyboard.
Making It Even Better
For a more robust accordion, consider these enhancements:
/* For smoother animations, consider using transform instead of max-height */
.accordion-content {
transform: translateY(-20px);
opacity: 0;
transition: transform 0.3s, opacity 0.3s, padding 0.3s;
}
.accordion-toggle:checked ~ .accordion-content {
transform: translateY(0);
opacity: 1;
}
/* Add hover effect for better UX */
.accordion-title:hover {
background: #efefef;
}
Browser Support
This CSS-only accordion works in all modern browsers. The technique relies on:
- CSS transitions (supported in all modern browsers)
- The CSS sibling selector (~) (widely supported)
- Checkbox state (fundamental HTML feature)
Conclusion
Creating an accordion with CSS alone is not only possible but fairly straightforward. It offers a lightweight solution without the overhead of JavaScript libraries.
By leveraging the hidden checkbox technique, you can create interactive components that respond to user input with pure CSS.
Remember that while this approach works well for simple accordions, more complex functionality (like ensuring only one panel is open at a time or controlling accordions programmatically) will still require JavaScript.
Walter Guevara is a Computer Scientist, software engineer, startup founder and previous mentor for a coding bootcamp. He has been creating software for the past 20 years.
Last updated on:
AD: "Heavy scripts slowing down your site? I use
Fathom Analytics because it’s lightweight, fast, and doesn’t invade my users privacy." -
Get $10 OFF your first invoice.