Accordions are useful when you want to present key information right away and give interested users the ability to learn more about the topic with a single click.
In this tutorial, you'll create a stylish accordion. Along the way, you'll learn how to combine ::before
and ::after
to create a beautiful effect, how to toggle class and content, and many more.
Prerequisites
- basic knowledge of HTML
- basic knowledge of CSS
- basic knowledge of JS
Pretty bullet
Let's start building with pretty bullet. First, create a new file accordion.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Beautifull accordion</title>
<link rel="stylesheet" href="accordion.css">
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="accordion.js">
</script>
</head>
<body>
<div class="accordion">
<div class="tab">
<h3 class="outer">
Coding and programming careers have great earning potential
<span class="inner"></span>
</h3>
<p class="hide">
One of the strongest and most obvious draws of learning to code is the earning potential for coding and programming professionals. The Bureau of Labor Statistics (BLS) tracks salary and other important workforce information for a variety of careers.
</p>
</div>
</div>
</body>
</html>
Accordion will be built inside <div>
with class accordion
. It contains another <div>
with class tab
that contains:
<h3 class="outer">
- it contains key information and<span>
that we'll be used create pretty bullet.<p class="innder">
- it contains more detailed information shown under the key information after click
At the moment there's a single accordion element. You'll add more of them later on.
After that, create a new file accordion.css:
.outer {
position: relative;
padding-left: 50px;
line-height: 40px;
color: #082c42;
}
.inner {
position: absolute;
left: 0;
}
.inner::before {
content: url("images/button.svg");
width: 40px;
height: 40px;
position: absolute;
}
.inner::after {
content: "•";
position: absolute;
width: 40px;
text-align: center;
font-size: 40px;
color: #082c42;
}
Here you have 4 elements on 3 levels:
.outer
is a container whose only purpose is to hold some text and.inner
span..inner
that holds::before
/::after
::before
and::after
.outer
is a container for your single bullet. The position of all the absolute positioned elements is based on a non-statically positioned ancestor. By setting the position to relative, you don't move the container, but you make it non-static, so all our absolute positioned elements will be positioned regarding the .outer
. You set the line-height on .outer
so all descendants will have the same line-height - that makes it easier to align them. With padding on the left, you get a little breathing room where you will now have space to put your pretty bullet.
.inner
's purpose is to contain ::before
and ::after
. It doesn't hold anything but that. Both, ::before
and ::after
will be positioned furthest to the left, thus we position their parent as absolute with left: 0
.
With the ::before
's help, we show that pretty 3D button.
Next, create a new directory "images". After that, download the image for the bullet here and store it inside "images".
Because it's an SVG you're able to set width and height. That works because the element is positioned absolutely. By default, <span>
is an inline element and you can't define its width or height. But any element that is positioned absolutely, automatically acts as a block element.
::after
is containing a bullet(•). By making the element the same width and height you make it easier to put it in the middle of the larger circle just with text-align: center
Read about
::before
and::after
here
Add JavaScript
Once you hace HTML and CSS ready you need to add JavaScript. Add a new file accordion.js.
$(document).ready(function() {
$( ".outer" ).click(function() {
$(this).closest(".tab").toggleClass("active");
})
})
The color of the middle circle on the bullet should change when clicked on. You can accomplish that by adding another class on the parent tab of the clicked <h3 class="outer">
. So when the user click on the <h3 class="outer">
, parent tab is found by $(this).closest(".tab")
. toggleClass
is a wonderful jQuery function, that adds or removes a class.
You need to include jQuery and accordion.js inside accordion.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Beautifull accordion</title>
<link rel="stylesheet" href="accordion.css">
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="accordion.js">
</script>
</head>
<body>
<div class="accordion">
<div class="tab">
<h3 class="outer">
Coding and programming careers have great earning potential
<span class="inner"></span>
</h3>
<p class="hide">
One of the strongest and most obvious draws of learning to code is the earning potential for coding and programming professionals. The Bureau of Labor Statistics (BLS) tracks salary and other important workforce information for a variety of careers.
</p>
</div>
</div>
</body>
</html>
Open accordion.html in your browser with Developer's tools opened. Try toggleClass
functionality by clicking on <h3 class="outer">
. You'll see that class gets added or removed on the .tab
when you click. Visualy, there are no changes yet. To do that you have to target the smaller circle with .inner::after
when .tab.active
in your css and set it to different color:
.tab.active .inner::after {
color: #bfd7ed;
}
Now, if you click on it repeatedly, it alternates colors based on class active
.
Hide detailed content
Detailed content should not show at first. It should slowly slide down when you click on <h3 class="outer">
(when the tab is active
) and slide back up when you click on it again.
To do that you need to add a new class to accordion.css:
.hide {
display: none;
}
To add slide effect you need to use slideToggle
function. Edit your accordion.js to look like this:
$(document).ready(function() {
$( ".outer" ).click(function() {
$(this).closest(".tab").toggleClass("active");
$(this).siblings(".hide").slideToggle("slow");
})
})
The added line is pretty similar to the previous one. It finds a sibling of the clicked element that has a class hide
and toggles the slide. Now, when you click on the .outer
, the bullet changes color, and the content either opens or closes.
Accordion
You can't really call it an accordion if it contains only one tab. so let's fix this. Edit your accordion.html to look like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Beautifull accordion</title>
<link rel="stylesheet" href="accordion.css">
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="accordion.js">
</script>
</head>
<body>
<div class="accordion">
<div class="tab">
<h3 class="outer">
Coding and programming careers have great earning potential
<span class="inner"></span>
</h3>
<p class="hide">
One of the strongest and most obvious draws of learning to code is the earning potential for coding and programming professionals. The Bureau of Labor Statistics (BLS) tracks salary and other important workforce information for a variety of careers.
</p>
</div>
<div class="tab">
<h3 class="outer">
Demand remains strong for coding-related jobs
<span class="inner"></span>
</h3>
<p class="hide">
When compared to the national average of five percent growth, you can see that a handful of positions are outpacing a lot of other careers. Computer programmers are an interesting outlier from this group, but some believe these projections are influenced by computer programming skills blending into other related in-demand tech roles. </p>
</div>
<div class="tab">
<h3 class="outer">
Coding ability gives new perspective to problem-solving
<span class="inner"></span>
</h3>
<p class="hide">
Coding, in its most basic terms, is just assigning a computer a task to do based on the logical guidelines you’ve outlined. Highly complex tasks are essentially a collection of smaller operations once you break them down. This methodical and logic-heavy approach to problem-solving can be a boon for figuring out problems beyond a coding challenge. </p>
</div>
</div>
</body>
</html>
Your HTML stayed the same, except that you added two more tabs inside the accordion. Check your browser, you'll see that it still works as intended.
There's only one problem - it doesn't look like an accordion. It looks like a bunch of text that can be shown or hidden. To improve that, let's create a line on the left side that will connect all tabs.
Line on the left
The line should run at the middle of your bullets and be covered by them where necessary. If you want the freedom to move it as you like, you can't create it as a border but as its own element. You'll use ::before
for creating it. Add these classes to accordion.css:
.accordion {
position: relative;
}
.accordion .tab::before {
border-left: solid #979797 1px;
content: '';
position: absolute;
left: 20px;
top: 0;
bottom: 0;
z-index: -1;
}
Although it looks like one line, it's created by multiple lines - from one tab to another. You want to take them(it) out of the page flow, so they're positioned absolutely. As mentioned before, you need to position an ancestor as relative
for this to work - .accordion {position: relative;}
. If you skip that step, you'll see that the line stretches through the whole browser.
The ::before
doesn't have any content, just left border. left: 20px;
positions it in the middle of the circle.
z-index: -1;
puts it behind the bullet and top: 0; bottom: 0;
stretches it from beginning to the end of the tab.
It looks pretty when it's closed, right? Not so much when it's opened.
Let's fix that.
Small CSS repairs
You want to move the detaild text to the right, so it aligns with the key point text. To do that, add padding to .hide
class inside accordion.css:
.hide {
padding-left: 50px; /* newly added */
display: none;
}
You also want to make the accordion narrower. To do that, limit max width of .accordion
to 800px inside accordion.css:
.accordion {
position: relative;
max-width: 800px;
}
This looks much better, doesn't it?
Only one tab opened
For now, if you click on a tab, it opens and if you click on it again, it closes. Clicking on another tab has no impact on all the other tabs. But there should be only one tab open at a time. When you click on one tab it should open and all the others should close.
Once again, edit accordion.js:
$(document).ready(function() {
$( ".outer" ).click(function() {
$(this).closest(".tab").toggleClass("active");
$(this).siblings(".hide").slideToggle("slow");
$(this).closest(".tab").siblings(".tab.active").find(".hide").slideUp("slow") // added
$(this).closest(".tab").siblings(".tab.active").removeClass("active") // added
})
})
From the <h3>
on which you clicked, it moves to its tab parent. Then it finds tab's siblings that have class active
. If you did that correctly, there shouldn't be more than one active tab. After that, it slides up the child with class hide
, and then it removes the active
class from the found tab.
If you play around a little in your browser, you can see that now there is always max one tab open, but you can also close the open one.
If you paid attention, you'll notice that you're not finished yet. There's a little skip when the text is sliding up and it doesn't look good.
That's because you have an unwanted margin for <h3>
. It is added by the browser.
Last CSS fixes
Because there's only one thing on your page, you won't include a whole file but just remove the border instead. Add margin: 0;
to .outer
inside accordion.css:
.outer {
position: relative;
padding-left: 50px;
line-height: 40px;
color: #082c42;
margin: 0; /* newly added */
}
Another browser-applied border that makes things look wrong is margin-top
on .hide
. Set this to 0, too:
.hide {
padding-left: 50px;
display: none;
margin-top: 0; /* newly added */
}
As you can see, you set the margin of .outer
to 0 and when the text is closing it doesn't skip anymore. You also broke design, but that's easily fixable. Add a little padding at the bottom of .outer
:
.outer {
position: relative;
padding-left: 50px;
line-height: 40px;
color: #082c42;
cursor: pointer;
margin: 0;
padding-bottom: 15px; /* newly added */
}
Now you created another problem. Because of the padding, the line on the left side doesn't end with the last bullet. Padding should be applied for all the bullets except for the last one. You can find the last tab with :last-child
and apply 0 padding at the bottom of its child .outer
.
.tab:last-child .outer{
padding-bottom: 0;
}
And this is it! Here's your pretty accordion:
In a real application, you'd probably want to use a CSS Reset file like this one or maybe a simpler one.
Conclusion
You have a pretty accordion to show, but you also learned so many things that you can use anywhere. You learned:
- How to combine
::before
and::after
to create a beautiful effect - How to toggle class
- How to apply different style by adding a class with JS
- How to toggle hidden/shown content
- That sometimes browsers break your design by applying their own
I hope you'll be able to create many more beautiful things with the knowledge you gained in this tutorial.
Whole code is visible here
Text taken from: rasmussen.edu/degrees/technology/blog/why-l..