Accessible progressive disclosure
Over on The Session I have a few instances of a progressive disclosure pattern. It’s just your basic show/hide toggle: click on a button; see some more content. For example, there’s a “download” button for every tune that displays options to download the tune in different formats (ABC and midi).
To begin with, I was using the :checked pseudo-class pattern that Charlotte has documented so well. I really like that pattern. It feels nice and straightforward. But then I got some feedback from someone using the site:
the link for midi files is no longer coming up on the tune pages. I am blind so I rely on the midi’s when finding tunes for my students.
I wrote back saying the link to download midi files was revealed by the “download” option. The response:
Excellent. I have it now, I was just looking for the midi button which wasn’t there. the actual download button doesn’t read as a button under each version of the tune but now I know it’s there I know what I am doing. I am using the JAWS screen reader.
This was just one person …one person who took the time to write to me. What about other screen reader users?
I dabbled around with adding role=”button” to the checkbox or the label, but that felt really icky (contradicting the inherent role of those elements) and it didn’t seem to make much difference anyway.
I decided to refactor the progressive disclosure to use JavaScript instead just CSS. I wanted to make sure that accessibility was built into the functionality, rather than just bolted on. That’s why code I’ve written doesn’t rely on the buttons having a particular class value; instead the buttons must have an aria-controls attribute that associates the button with the element it toggles (in much the same way that a for attribute associates a label with a form field).
Here’s the logic:
- Find any elements that have an aria-controls attribute (these should be buttons).
- Grab the value of that aria-controls attribute (an ID).
- Hide the element with that ID by applying aria-hidden=”true” and make that element focusable by adding tabindex=”-1".
- Set aria-expanded=”false” on the associated button (this attribute can be a bit confusing — it doesn’t mean that this element is not expanded; it means the element it controls is not expanded).
- Listen for click events on those buttons.
- Toggle the aria-hidden and aria-expanded when there’s a click event.
- When aria-hidden is set to false on an element (thereby revealing it), focus that element.
You can see it action on CodePen.
I’m still playing around with this. I think the :focus styles are probably far too subtle right now — see this excellent presentation from Laura Palmaro for more on that. I’m also not sure if the revealed content should automatically take focus. I’ll see if I can get some feedback from people on The Session using screen readers — there’s quite a few of them.
Feel free to use my code but you might want to check out Jason’s code to do the same thing — his is bound to be nicer to work with.
This was originally posted on my own site.