In this article, we will create an accordion using CSS. Another common component that we create using minimal code. Most of the tricks rely on <input>
hacks but we will do something different here.
The above figure illustrates what we are going to build together.
The HTML markup
For this component, the important part will be the HTML. We are going to use the <details>
element that already comes with what we want. Our code will look like below:
<div class="accordion">
<details>
<summary>Sesame snaps</summary>
<div> ... </div>
</details>
<details>
<summary>Chocolate bar</summary>
<div>...</div>
</details>
<details>
<summary>Donut gummies</summary>
<div>...</div>
</details>
</div>
If you run the code and click on the titles you can expand/collapse the content
We are almost done! We already have the functionality of an accordion using a native HTML element.
The
<details>
HTML element creates a disclosure widget in which information is visible only when the widget is toggled into an "open" state. A summary or label must be provided using the<summary>
element. ref
Now, all we need to do is to style it.
Styling the accordion
The <details>
element comes with two interesting features that we are going to use to style our accordion.
First, we have the ::marker
element which is the small triangle next to the title. Each browser will define a default style to the ::marker
that changes when we expand/collapse the element.
Then, we have the open
attribute. The latter is automatically added to the element when this one is opened.
We can also explicitly define that attribute if we want the element opened by default.
The open
attribute is also useful to target the expanded <details>
and apply a specific style to it.
Let’s start with some basic CSS:
details {
margin: 5px;
font-size: 18px;
}
details > * {
padding: .75rem;
}
details > div {
background: #ddd;
border-radius: 0 0 5px 5px;
}
summary {
border-radius: 5px;
font-size: 20px;
font-family: sans-serif;
font-weight: bold;
color: #fff;
background: #0B486B;
cursor: pointer;
}
details[open] summary {
border-radius: 5px 5px 0 0;
}
Nothing complex so far. A few color and font changes for a good-looking accordion:
Notice the use of
details[open] summary {
border-radius: 5px 5px 0 0;
}
This allows me to define a different border-radius
for the <summary>
element when the <details>
is opened.
Now let’s update the ::marker
. To do this, we have two methods
The first one is to override the content like the below:
summary::marker {
content: "+";
}
summary[open]::marker {
content: "-";
}
This method is the easiest one but it’s limited. We can change the arrow with any character we want (even an emoji) but we cannot style it like we want due to some restrictions related to the ::marker
element.
Only certain CSS properties can be used in a rule with
::marker
as a selector: ref
To overcome this limitation we can rely on the second method. We start by making the content of ::marker
empty
summary::marker {
content: "";
}
Then we rely on pseudo-elements to create our own indicator.
details summary::before {
content:"";
/* styles when the element is closed */
}
details[open] summary::before {
/* styles when the element is opened */
}
Now, we can control the CSS as we want. Below is a basic example where I am creating a “plus” and “minus” symbol using gradients (I used the same technique in a previous article)
In addition to the style, I am also changing the position of the indicator to make it on the right. We can also apply some subtle animations to make it look better
I can hear you asking “How to add an animation when open/close the accordion?”. The answer is we cannot but things may change in the future.
Unfortunately, at this time, there's no built-in way to animate the transition between open and closed. ref
We can find some workaround to simulate an animation but for the sake of simplicity, I won’t detail them in this article.
Adding a bit of JavaScript
Now that we have our accordion we can add an optional JavaScript code in case we want to keep only one section opened at a time. There is no relation between the <details>
elements and you have probably noticed that we can have all of them open at the same time. If you don’t want such behavior you can add a small JavaScript code:
let details = document.querySelectorAll('.accordion details')
details.forEach(function (d, index) {
d.onclick = () => {
details.forEach(function(c, i) {
index === i ?'':c.removeAttribute('open')
});
};
});
We loop through all the details elements and we add an onClick event to each one. Inside the onClick even we perform another loop. For each details element, if it’s different from the clicked one, we remove its open attribute.
Conclusion
The article ends here! We have transformed a simple code into a functional accordion. We didn’t clutter our code with a complex logic. We simply used the native <details>
element with a bit of JavaScript at the end. The only drawback is the lack of transition when we open/close each element.
Frequently Asked Questions
Should I build my website with PHP?
Is Joomla Suitable for E-commerce Websites?
How do I make my website HTTPS?
How does adapting websites for mobile users contribute to successful e-commerce website localization?
Temani Afif is an expert web developer, a content creator, and a CSS addict. He is the mastermind behind CSS Loaders, CSS ... show more
Temani Afif is an expert web developer, a content creator, and a CSS addict. He is the mastermind behind CSS Loaders, CSS Generators, CSS Tip and a lot of CSS stuff.
View all posts by Temani Afif