DEV Community

Cover image for How to build an Accordion Menu using HTML, CSS and JavaScript

How to build an Accordion Menu using HTML, CSS and JavaScript

Kingsley Ubah on October 07, 2021

HTML, CSS and JavaScript can help you create stylish and dynamic web elements. One of those kind of element is an Accordion Menu. In this tutorial...
Collapse
 
auroratide profile image
Timothy Foster

Very well-made tutorial! Love the imagery and how you annotate/explain the code blocks.

I definitely recommend looking into the details HTML element. Besides no longer needing Javascript, it also makes the accordion more accessible, allowing assistive tools like screen readers to announce when something can be collapsed or expanded.

Using just divs, you would have to manually add aria roles, labels, and possibly controls to achieve the same thing.

Collapse
 
merri profile image
Vesa Piittinen

Details + summary cannot be used as accordion in an accessible fashion, because <summary /> element has role="button" which means all semantics are lost, and accordions require proper headings for expected navigation behavior. Details works for a single open/collapse element, but not for a full accordion. It is a bit of a shame though, it is almost there to work as a basis for accordion.

See accordion example and requirements for all the things you need to meet for a good accordion implementation.

Collapse
 
auroratide profile image
Timothy Foster

Indeed you are right! I hadn't realized that technicality between summary and heading, and thought putting a heading in the summary would be enough.

There are other big things missing from a pure details+summary implementation, like collapsing other details and navigation with arrow keys. I suppose it depends on the degree to which a true accordion is needed or if a set of collapsibles is enough.

Collapse
 
supportic profile image
Supportic • Edited

Since the button sits inside the H3 you can place role="heading" aria-level="3" on summary and inside just a button to trigger which takes the full width.
Even if it's saying to use a H3 it doesn't make sense to me because this raises the question what would you do when you want to use H2 inside the accordion? You are not allowed to skip headlines as far as I know ^^
Also in the example since they are not using icons displaying with content attribute in CSS, why isn't there aria-hidden true on that icon?

Thread Thread
 
merri profile image
Vesa Piittinen

The h3 is somewhat irrelevant, you can use other heading levels depending on the hierarchy. h3 however is the most likely level for accordions.

It doesn't make sense to put role="heading" aria-level="3" on summary because then you lose all the reasons for using the details + summary combo, which would be to get the functionality without JavaScript. As you change the semantics you'd also need to add aria-expanded, and at that point it would be the same if you were doing the thing using just div elements. Also I think you'd still lose the semantics inside summary element, so the button inside would not be announced as a button.

In short the point of using native HTML elements is to embrace what the existing features give you for free, such as <button /> providing all the keyboard goodness, tabbability, and submitting forms. And that last one you can opt-out with type="button". Aria is a hard hitting feature that should be the last resort, because when you use it you have to take responsibility of much more to get things right = learn to test the experience using a screenreader.

The "icon" has no content. It is an empty span so there is nothing a screenreader would take by mistake. So there is no need for aria-hidden="true". You only need aria-hidden="true" if there is a chance for irrelevant things to be announced (and thus worse user experience).

Collapse
 
ubahthebuilder profile image
Kingsley Ubah

Hi Timothy,

Thanks a lot.

Collapse
 
andrewpierno profile image
Andrew Pierno

Hey Kingsley, this is a solid tutorial!
Would you be interested in writing some tutorials for our companies? Happy to pay! You can either DM me on twitter at twitter.com/AndrewPierno or fill out this little airtable form airtable.com/shrN6S3NMZ7oxRXTt.

Collapse
 
ubahthebuilder profile image
Kingsley Ubah

Sent you a DM on Twitter.

Collapse
 
bakoun profile image
BaKouN

Or you could use the summary html tag who does all of that work for you ! No JS that way !

Collapse
 
ubahthebuilder profile image
Kingsley Ubah

Makes sense.

Collapse
 
supportic profile image
Supportic

What if the content exceeds the 150px height?

Collapse
 
ubahthebuilder profile image
Kingsley Ubah

Height could be increased.

Collapse
 
harzjunior profile image
harzjunior

you can use overflow y auto, this will allow you to scroll down once your text exceeds the height limit

Collapse
 
ubahthebuilder profile image
Kingsley Ubah

Perfect!

Collapse
 
obaino82 profile image
Obaino82

👍

Collapse
 
tohka200213 profile image
tohka20-0213

In JS part, why did you get elements as HTMLCollection by using getElementsByClassName method?

In this case, I get a NodeList instead and use a forEach method. Because the code becomes simpler.

Collapse
 
rishnegi7711 profile image
rishnegi7711

Hi!

I am new to front end developement, can someone please tell why we didn't use ::after because I changed it to ::after in my code and couldn't see any difference in the layout

Collapse
 
akulsr0 profile image
Akul Srivastava

Nice!!! I also made one, with slightly different approach.
vanillaweb.vercel.app/accordion

Collapse
 
ubahthebuilder profile image
Kingsley Ubah

That looks very nice!

Collapse
 
godwinkachi profile image
Godwin 'Kachi

Great tutorial

Collapse
 
ubahthebuilder profile image
Kingsley Ubah

Thanks.

Collapse
 
awdev profile image
AWDEV

Aw Project simple code HTML.

Audio

Product Simple Www.alhikmah.my.id/p/mp3-al-quran.html

Collapse
 
koas profile image
Koas

A great step by step guide, great work!

Collapse
 
ubahthebuilder profile image
Kingsley Ubah

Thank you!

Collapse
 
eclisauce profile image
Markus Andersson

A alternative way is to skip the javascript and go with the label & checkbox solution, since it can get the same behavior without javascript