Tree views in CSS
Styling a list of nested details
elements to create a beautiful lokking tree view, all in CSS, all nicely accessible.
Styling a list of nested details
elements to create a beautiful lokking tree view, all in CSS, all nicely accessible.
It’s been almost two years since I added audio playback on The Session. The interface is quite straightforward. For any tune setting, there’s a button that says “play audio”. When you press that button, audio plays and the button’s text changes to “pause audio.”
By updating the button’s text like this, I’m updating the button’s accessible name. In other situations, where the button text doesn’t change, you can indicate whether a button is active or not by toggling the aria-pressed
attribute. I’ve been doing that on the “share” buttons that act as the interface for a progressive disclosure. The label on the button—“share”—doesn’t change when the button is pressed. For that kind of progressive disclosure pattern, the button also has an aria-controls
and aria-expanded
attribute.
From all the advice I’ve read about button states, you should either update the accessible name or change the aria-pressed
attribute, but not both. That would lead to the confusing situation of having a button labelled “pause audio” as having a state of “pressed” when in fact the audio is playing.
That was all fine until I recently added some more functionality to The Session. As well as being able to play back audio, you can now adjust the tempo of the playback speed. The interface element for this is a slider, input type="range"
.
But this means that the “play audio” button now does two things. It plays the audio, but it also acts as a progressive disclosure control, revealing the tempo slider. The button is simultaneously a push button for playing and pausing music, and a toggle button for showing and hiding another interface element.
So should I be toggling the aria-pressed
attribute now, even though the accessible name is changing? Or is it enough to have the relationship defined by aria-controls
and the state defined by aria-expanded
?
Based on past experience, my gut feeling is that I’m probably using too much ARIA. Maybe it’s an anti-pattern to use both aria-expanded
and aria-pressed
on a progressive disclosure control.
I’m kind of rubber-ducking here, and now that I’ve written down what I’m thinking, I’m pretty sure I’m going to remove the toggling of aria-pressed
in any situation where I’m already toggling aria-expanded
.
What I really need to do is enlist the help of actual screen reader users. There are a number of members of The Session who use screen readers. I should get in touch and see if the new functionality makes sense to them.
Progressive disclosure interface patterns categorised and evaluated:
I really like the hypertext history invoked in this article.
The piece finishes with a great note on the MacNamara fallacy:
Everyone thinks metrics let us measure results. But, actually, they don’t. They measure only what they are measuring. Engagement, for example, is not something that can be measured, so we use an analogue for it. Time on page. Or clicks.
We often end up measuring what is quick, cheap, and easy to measure. Therefore, few organizations regularly conduct usability testing or customer-satisfaction surveys, but lots use analytics.
Even today, organizations often use clicks as a measure of engagement. So, all too often, they design user interfaces to generate clicks, so the system can measure them.
When we hide content, there’s a greater risk the user won’t see it. There’s a higher reliance on digital literacy and it’s generally more labour intensive for the user.
Worse still, sometimes we kill off essential content.
This is a really nice write-up of creating an accessible progressive disclosure widget (a show/hide toggle).
Where it gets really interesting is when Andy shows how it could all be encapsulated into a web component with a progressive enhancement mindset
I love this deep dive that Sara takes into the question of marking up content for progressive disclosure. It reminds me Dan’s SimpleQuiz from back in the day.
Then there’s this gem, which I think is a terrificly succinct explanation of the importance of meaningful markup:
It’s always necessary, in my opinion, to consider what content would render and look like in foreign environments, or in environments that are not controlled by our own styles and scripts. Writing semantic HTML is the first step in achieving truly resilient Web sites and applications.
Comparing different ways to hide content accessibly:
There are three reasons behind hiding content in an interface, and it’s important to identify what those reasons are, as they will correlate with the appropriate technique needed to hide such content.
- Temporarily Hidden Content
- Purposefully Visually Hidden Content
- Purposefully Visual-Only Content
I’m no fan of mega menus, and if a site were being designed from scratch, I’d do everything I could to avoid them, but on some existing projects they’re an unavoidable necessity (the design equivalent of technical debt). In those situations, this looks like a really nice, responsive approach.
I wrote a while back about how I switched from using a button to using a link for progressive disclosure patterns. That looks like it was a good move—if I use a button, I’d need to use aria-controls
and, as Heydon outlines here, the screen reader support is pants.
One way of implementing the growing/shrinking navigation pattern—an alternative to just shoving everything behind a hamburger icon.
I wrote a little while back about making an accessible progressive disclosure pattern. It’s very basic—just a few ARIA properties and a bit of JavaScript sprinkled onto some basic HTML. The HTML contains a button
element that toggles the aria-hidden
property on a chunk of markup.
Earlier this week I had a chance to hang out with accessibility experts Derek Featherstone and Devon Persing so I took the opportunity to pepper them with questions about this pattern. My main question was “Should I automatically focus the toggled content?”
Derek’s response was very perceptive. He wanted to know why I was using a button
. Good question. When you think about it, what I’m doing is pointing from one element to another. On the web, we point with links.
There are no hard’n’fast rules about this kind of thing, but as Derek put it, it helps to think about whether the action involves controlling something (use a button
) or taking the user somewhere (use a link). At first glance, the progressive disclosure pattern seems to be about controlling something—toggling the appearance of another element. But if I’m questioning whether to automatically focus that element, then really I’m asking whether I want to take the user to that place in the document—in other words, linking to it.
I decided to update the markup. Here’s what I had before:
<button aria-controls="content">Reveal</button>
<div id="content"></div>
Here’s what I have now:
<a href="#content" aria-controls="content">Reveal</a>
<div id="content"></div>
The logic in the JavaScript remains exactly the same:
aria-controls
attribute (these were buttons, now they’re links).aria-hidden="true"
and make that element focusable by adding tabindex="-1"
.aria-expanded="false"
on the associated link (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).aria-hidden
and aria-expanded
when there’s a click event.aria-hidden
is set to false on an element (thereby revealing it), focus that element.You can see it in action on CodePen.
See the Pen Accessible toggle (link) by Jeremy Keith (@adactio) on CodePen.
Here’s a bit of convergent evolution: Hugo’s script is similar to what I wrote about recently.
He also raises a point that Kevin mentioned:
I would like to investigate on the
details
andsummary
elements as they are basically a native implementation for content toggles.
For some reason details
never got much browser love, even though it’s clearly paving a well-trodden cowpath.
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:
aria-controls
attribute (these should be buttons).aria-controls
attribute (an ID).aria-hidden="true"
and make that element focusable by adding tabindex="-1"
.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).aria-hidden
and aria-expanded
when there’s a click event.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.
Update: In response to this discussion, I’ve decided not to automatically focus the expanded content.
A clever way of doing progressive disclosure with CSS.