Disclosure widgets are one of the most common component patterns you can find on the web. It consists of a button that can hide or show information when you click it. It's also one of the straightforward components to make from a technical standpoint.
Just a quick note: this article will focus on the most basic form of it to show or hide content. A navigation menu can be considered as a disclosure widget but it has other features that won’t be the main focus of this article.
This component pattern has two approaches: the first one is creating a
<button> element with the attribute aria-expanded that will toggle between
false when you want the content to be shown or hidden. Additionally, you want to create a relationship between those two elements with aria-controls to indicate the button controls the presence of this element, like this:
aria-expanded attribute. However, we can pair it with a pure CSS’ next-sibling combinator to control showing and hiding the disclosure’s content.
All of this sounds like quite some work for a relatively simple component pattern, it'd be cool if we had a native option to make a disclosure widget... Oh, wait, we have one! This is where our second approach enters, and that's using the
<summary> elements. Let's come back to our initial markup and let's modify it with this approach.
And that's it! This disclosure widget works, and it's accessible, so I guess the answer would be using this one instead of using the first approach, right? Well, as usual, the reality is more nuanced than that. As much as I'd prefer to use native options for component patterns, the answer to that question is a definitive “It depends”.
When picking a disclosure widget markup, the author needs to balance the strengths and limitations of each approach, and consider what content it will contain. This article will talk about those nuances you should keep in mind about choosing one markup approach or another, mostly focusing on screen reader accessibility because both of them meet the requirements of keyboard navigation with no problem at all.
The first factor to keep in mind is consistency. When you use a progressively enhanced button approach, you know that it'll be the same for screen readers. Both markup approaches will read them as a button and will read the
aria-expanded attribute similarly. But with
<summary> things change quite a bit when you start taking into consideration different screen readers and browsers.
When you use
<summary>, NVDA and JAWS both narrate it as a button in Chrome, and they'll mention if the element is collapsed or expanded. But when you use the same markup in Firefox, they narrate the marker (that is, the visual indicator is being used to indicate the element is collapsed or expanded) as well. My previous example will be read as “Filled right pointing small triangle, what are cephalopods, button, collapsed”. And when we start talking about mobile, the experiences vary a lot.
This is not bad per se, but in some browsers, mentioning the marker can feel a bit like noise, but buttons on the other hand offer a cleaner more consistent experience.
The key point in this section is that you should ask yourself this question: am I okay with letting the screen reader experience be different between different screen readers and browsers? If you're all right with this, use
<summary>, but if you'd prefer a more consistent experience all around, use a progressively enhanced
<summary have this triangle arrow that will change when it is expanded or collapsed. Maybe you have a design that has a different indicator, can you modify that? The answer is yes, but there is a huge caveat here:
As Manuel mentions in his article details/summary inconsistencies, you can modify the default arrow by using those CSS rules:
This will hide the arrow in all browsers except Safari, so you’ll need an additional CSS rule:
This deletes the indicator in all browsers, but this brings a big problem: as Manuel's tests in the same article show, some combination of screen readers and browsers will use the arrow indicator as part of the accessible name, and if you remove them, it'll stop indicating its state once the user press the
This behavior is particularly notorious with VoiceOver and Firefox because when you remove the marker, it'll indicate nothing when the user expands the content.
For this reason, my suggestion here is that if the design has a different indicator for the expanded/collapsed states than the default option, you should use a progressively enhanced
<button> approach. On the other hand, if you are ok with the default marker, you can stick with
Those previous points are making look
<summary> as an unreliable option, but there is something that those elements add to the table that a progressively enhanced button option can't.
As Manuel mentions in his article the details element and in-page search, Chromium-based browser in-page search option can look for content even when the
<summary> element is collapsed. This is an important factor to keep in mind because this improves usability for someone who uses the in-page search.
As a side note, do you remember when I said I would not focus on disclosure widget variants like a navigation menu? Well, between the different ways a screen reader displays this component and now taking into consideration the in-page search option, makes
<summary> unsuitable for a navigation menu.
So, the key point of this section: if you think letting the user find a keyword using the browser in-page search is important, stick with
<summary>, otherwise, use a progressively enhanced button.
The main selling point of
The first example that comes into my mind is a content warning if it's visible, can cause legitimately bad experiences or trigger a PTSD (Post traumatic stress disorder) episode for a person.
<summary> in her article, A content warning component.
My key point here is that you need to consider how critical is to hide the content as an initial state, no matter what. If it's necessary, consider using
<summary>, otherwise, using a progressive enhanced button is a good option.
There is quite an interesting thing you can do with a disclosure widget made with the progressively enhanced
<button>, and that's adding headings on them. This makes it easier for screen reader users to navigate to this part of the content. The process changes a bit depending on what approach you want to use. For this approach, the markup would look like this:
There is a catch though: the
<button> is now inside the heading, so it’ll be undetected by the next-sibling combinator. There are two options: we can use to solve this issue:
Modifying our script: We can make a slight modification in our script and making the adjacent container have the
hidden attribute .Then, we toggle it when the
<button> has the proper
aria-expanded value, like this:
:has(): The other option is using the (relatively) new CSS
:has() selector like this:
The problem with
:has() is its browser support. At the moment of writing this article, Firefox just shipped it in the latest version (121), so browser support may make use of this selector not robust enough to be reliable (for now!).
Can you do that with
<summary>? Yes, do I recommend it to do it? No, it has some inconsistent and even buggy behaviors when you use a screen reader to activate them.
Let's start with checking how it is “possible”, first, we add our heading inside
<summary> like this:
Keep in mind you need to add the
<summary> element as the direct child of
<details>, otherwise, it’ll not be recognized as our disclosure’s trigger. Now, There is one detail, this is how it'd look:
Then, we'd need to use CSS to put the heading at the same level, but if you do it with, for example,
display: flex, it'll not show the marker, and as we saw before, this will affect screen reader experience for some combinations of screen reader and browser. My approach here was making the heading inline, and that worked well, like this:
Why I don't recommend doing that with
<summary>? With both approaches, you can use screen reader shortcuts to navigate directly to a heading, but there is a caveat here:
<button> approach there will be no problem: the button is inside the heading and if the screen reader's virtual cursor is over the heading, you can control the button by pressing the
Space keys. This is useful when you use screen reader’s shortcuts to navigate to a heading (like using the H key).
There catch here comes with
<summary> because it has way less consistent results. To prove that, I decided to test each disclosure with heading markup next to each other. You can take a look at the example I used right here:
This test used the next combinations of screen readers and browser:
And those are the results:
<summary> element, making this feature not advisable.
<details> element it worked normally, so it’s safe to say adding a heading inside
<summary> is not advisable.
<summary> element that way. You can activate it navigating with the
Tab key but for some reason, if VoiceOver visual cursor is on the heading itself, it can’t be activated. This can create confusion for screen reader users because they found a heading, but they will not find extra content below it because it's a disclosure widget.
So, key point of this section: if you think adding heading navigation to your disclosure widget improves the user experience for screen reader users, you'd be better using a
<button> element, otherwise, use
Having a native way to make a disclosure widget is great! But as usual, things are not that easy because browsers' implementation tend to vary in details that you need to take into consideration. It's not the first HTML element that has inconsistencies among browser (As an example, just look at Manuel's article O dialog focus, where art thou? to check how browsers implement keyboard focus with the
<dialog> element) and it certainly won't be the last.
In my mind, there is no doubt that browser’s implementation of
<summary>will be more consistent in the future and this article might end up being a thing of the past. But for now, there are important considerations you need to keep in mind before deciding to use one markup approach or another.