Accessible Showing and Hiding
Editor's Note: When I started this blog nearly three years ago, one of the first things I did was write a series on showing and hiding elements on a page. The posts were very basic, as was my knowledge at the time. At best, they demonstrated an incomplete answer to the question of how to selectively reveal content based on user interaction. At worst, they encouraged a solution without any regard to accessibility. That's why I was thrilled when I saw an article on the Filament Group blog describing their accessible collapsible content widget. They've graciously accepted my request to re-post that article here, so without any further ado, here it is…
Collapsible content areas are frequently presented in web sites and applications as a way to let users to control how content is shown or hidden on the page. Also called collapsibles, spin-downs, toggle panels, twisties, and content disclosures, they're ideal for selectively displaying optional information — like instructional text or additional details, for example — so users can focus on the task at hand and view this content only as needed.
The collapsible content area widget is fairly simple — a couple of HTML elements controlled with minimal CSS and JavaScript — but when we were researching our book, Designing with Progressive Enhancement, we discovered that many common approaches to creating collapsible content fail to incorporate accessibility features. Happily, there is a way to build collapsible content with progressive enhancement so it delivers an optimal accessible experience for sighted and screen reader users alike.
The code example described in this article is one of the 12 fully-accessible, project-ready, progressive enhancement-driven widgets that accompanies our new book, Designing with Progressive Enhancement.
View a demo
Before we dive in to the details, let's take a look at the widget in question. Here's an error dialog we created for a photo site, which appears when a photo upload fails — it provides a high-level summary, and a collapsible Details block with supplemental content about which photos failed to upload and why:
NOTE on the demo's "View low bandwidth" link: This demo runs on our EnhanceJS framework, which adds a "View low-bandwidth version" link to allow users to toggle from a basic to enhanced view, and drops a cookie on change. If you click the link to view the low-bandwidth version of the demo, you'll need to click it again to view the enhanced version of this site on future views. (Learn more about EnhanceJS here.)
So what's the problem here?
When we first set out to build an accessible collapsible content widget, we thought that the best way to hide the content and keep it accessible to screen readers would be to position it off-screen. We didn't appreciate that while technically this does make the content accessible, positioning content off-screen doesn’t actually provide an accessible experience.
People using assistive technologies like screen readers want to be able to interact with the page just as sighted users do. As accessibility specialist Adam Spencer noted in a recent CBS News story, “True accessibility is giving blind people the same options to access information as sighted ones†(emphasis ours).
While it’s true that hiding collapsible content off-screen at least keeps it in the page and accessible at a baseline level, it’s decidedly sub-par compared with the standard web user’s experience. For example, when a user with a screen reader encounters an accordion widget with ten sections, just like a sighted user they want to hear only the open section, not all content in every section. Screen reader users also expect to use the keyboard to quickly hear the accordion section headings — the screen reader equivalent of quickly scanning the page. When you simply hide content off-screen, these actions aren’t possible; instead, the screen reader reads all content in all the hidden panes, and the screen reader user has no option but to wade through it all in the order it appears in the markup.
Ideally, the experience for both sighted and visually impaired users should be as functionally similar as possible. The W3C Web Content Accessibility Group (WCAG) has outlined a set of four principles that must be met to ensure that content is accessible. Quickly, they say that it should be:
- Perceivable — Information and user interface components must be presentable to users in ways they can perceive.
- Operable — User interface components and navigation must be operable.
- Understandable — Information and the operation of user interface must be understandable.
- Robust — Content must be robust enough that it can be interpreted reliably by a wide variety of user agents, including assistive technologies.
That’s all well and good, but these definitions are a little abstract. What does it really mean? WCAG provides extended definitions that give us some helpful clues — for example, Perceivable content “can't be invisible to all of their sensesâ€; an Operable interface “cannot require interaction that a user cannot performâ€; an Understandable interface’s “content or operation cannot be beyond their understandingâ€; and it is Robust if “as technologies and user agents evolve, the content should remain accessibleâ€.
In light of these principles, many common techniques for presenting collapsible content fall short. Consider these scenarios:
- Hiding content by default in the CSS, and relying on JavaScript events to let the user display it. If JavaScript is disabled or unavailable, the user has no means of accessing the content, and may not even know it's there. In this case, content is neither perceivable nor operable — or worse, if the design provides a hint (like an open/close icon or "View details" link), the hint is perceivable but the content non-functioning.
- Hiding content by positioning it off the page (e.g.,
position: absolute; left: -9999px;
). Doing this ensures that the content is available to screen readers. However, it's always available — the user has no ability to control showing or hiding, or manage whether it's is read aloud. As collapsible content it's not operable; and depending on the widget's content, presenting it all simultaneously may not be understandable. - Providing only a visual indicator, like an icon, on the clickable element to show that it can manipulate visibility of related content. This works for sighted users, but fails for screen readers. Unless the icon is accompanied by some adequate auditory feedback that content can be shown/hidden, the feature may not be fully perceivable or understandable to the screen reader user.
- Applying JavaScript events to a non-natively-focusable element (such as a heading) to show/hide related content. While it works for mouse users, this approach does not guarantee that the widget is navigable for keyboard users (particularly in browsers that don't properly support the
tabindex
attribute), which are necessary for both screen readers and many mobile devices. In other words, it's potentially neither operable nor robust.
To create a collapsible content widget that works for everyone — and doesn't compromise the experience for screen reader users — we had to rethink what "accessibility" means when showing and hiding content.
Our approach
We start by marking up the page with semantic HTML elements for the heading and content blocks. For example, consider the collapsible widget portion of our error dialog, which consists of a heading element immediately followed by an unordered list: [html]Details
- purple-flower.tif Not a supported file format
- flower-photos.zip Not a supported file format
This markup provides a usable and natively accessible basic experience in all browsers.
If JavaScript is present, we use it to apply enhancements that transform this markup into a functioning collapsible widget with a number of accessibility features. The enhancement script appends several attributes and elements to the basic markup:
- classes are assigned to the heading element which hide the Details content and apply a visual cue (icon) to indicate that it can be expanded or collapsed
- a
span
tag is appended to the heading immediately before the text label which contains the word "Show" followed by a single space if the content is hidden by default; when the content is shown, the script dynamically updates this word to "Hide." This span is intended only for screen readers to audibly describe the heading's function as a toggle link (i.e., "Show Details"), so it's hidden from standard browsers with absolute positioning and a large negative left value - a standard anchor link element is wrapped around the heading content to allow it to receive keyboard focus and be accessed with the Tab key
- an
aria-hidden
attribute is assigned to the unordered list to ensure that it is truly hidden from ARIA-enabled screen readers when it is hidden from sight (aria-hidden="true"
). Whiledisplay: none;
will sufficiently hide the content in current screen readers, future screen readers may not continue to obey visual styles likedisplay
, so adding this attribute is considered good, fail-safe practice. - classes are also appended to the unordered list to show or hide it visually with CSS. To hide the content, we use the display: none CSS property so that the content is completely hidden from all users
The resulting enhanced markup looks like this:
[html]Show Details
- purple-flower.tif Not a supported file format
- flower-photos.zip Not a supported file format
And the enhanced classes for providing feedback are structured as follows – notice that we use a single background image sprite (icon-triangle.png), and simply adjust the background position to show the appropriate state:
[css] . . . .collapsible-heading { padding-left:15px; background:url(../images/icon-triangle.png) 0 6px no-repeat; } .collapsible-heading-collapsed { background-position:0 -84px; } .collapsible-heading-toggle { text-decoration:none; color:#333; } .collapsible-heading-status { position:absolute; left:-99999px; } .collapsible-content-collapsed { display:none; } . . . [/css]NOTE on testing browser capabilities: To ensure that the browser can fully support all enhancements, we recommend using our EnhanceJS framework, which tests browsers' CSS and JavaScript capabilities and applies enhancements only after those tests are passed. Read more about EnhanceJS here: Introducing EnhanceJS: A smarter, safer way to apply progressive enhancement.
What we accomplish
When built as described above, our collapsible content widget incorporates specific features to meet the four accessibility principles outlined by WCAG:
- To ensure that it’s perceivable and understandable, it includes cues for both visual and screen reader users that indicate the heading element will show and hide associated content. Visual users see an icon that changes orientation based on the content's state; for screen reader users, the script conditionally appends the word "Show" or "Hide" before the heading text to describe the content's state and provide appropriate language to indicate how the heading is used. Additionally, the widget provides feedback to ARIA-enabled screen readers with the ARIA attribute,
aria-hidden
; it's set to true when the content is hidden, and false when shown. - To make it operable, we ensure that the widget is fully navigable and controllable with the keyboard. When CSS and JavaScript enhancements are applied to the page, the script wraps each
heading
element's text with an anchor element so that the user can navigate to and focus on the heading with the Tab key, and hide or show related content with the Enter key. - And our progressive enhancement approach helps us ensure it’s robust: The foundation is semantic HTML, which is accessible and usable on all browsers; only when a browser is capable of enhancements is the markup transformed into a collapsible widget with enhanced styling and behavior.
Looking ahead
The HTML5 spec includes a new element called details
, which if implemented as proposed, will provide a native collapsible widget that will require no JavaScript and will be accessible without requiring any additional work. No browsers have adopted the details
element at this time, but it does present a potential alternative to this approach that we may be able to use in the future. Bruce Lawson provides an interesting summary of details
and an argument in favor of using semantic interactive elements over JavaScript.
How to use the plugin
Download the collapsible content plugin script (see instructions below), and reference it and the jQuery library in your page. You'll also need to append the CSS classes listed above for the collapsed state of the widget. NOTE: You'll likely need to edit this CSS to fit with your own project's design.
Call the collapsible plugin on the heading or other element that you're using to show related content; the plugin then assumes that the next element in the source order is the content block to show/hide. For example, on DOM ready you can call the plugin on all H2 elements:
[js] $(document).ready(function(){ $('h2').collapsible(); }); [/js]Where to download it
If you've already purchased Designing with Progressive Enhancement, you can download all twelve widgets at the code examples download page.
For the collapsible content plugin and all others that have been released publicly as open source, download the zip.
To learn more, check out Designing with Progressive Enhancement's open-source plugin Google Code repository: http://code.google.com/p/dwpe/.
Help us improve the code
We keep track of issues (feature requests or found defects) in the DWPE Google Code site. Please take a look at our list, and feel free to add to it if you find a bug or have a specific idea about how to improve the plugin that we haven't identified: http://code.google.com/p/dwpe/issues/list. If you think you can help on a particular issue, please attach a patch and we'll review it as soon as possible.
Thoughts? Feedback?
We'd love to hear what you think of this plugin. Please leave us a comment on our blog!