Accordions contain of a number of content panels, each of wich can be expanded or collapsed vertically by the user.
Accordions help to save vertical space and prevent from visual noise. Some accordions allow only a single panel to be expanded at a time, others allow multiple.
Before you continue, please read Tablist widgets (or: tab panels, tabs) to understand why accordions simply are extended variants of tablists, providing a slightly different layout and (sometimes) expandability of multiple panels.
<p><button>Focusable element before</button></p><divclass="accordion"><h1class="visually-hidden">
flowers (accordion)
</h1><pclass="visually-hidden">
Tablist help: use the accordion controls to toggle the visibility of their respective panels (below the controls).
</p><divclass="control"><inputchecked=""class="visually-hidden"id="accordion_flowers_panel_rose"name="accordion_flowers"type="radio"value="accordion_flowers_panel_rose" /><h2><labelfor="accordion_flowers_panel_rose"><spanclass="visually-hidden">Show panel </span>rose</label></h2></div><divclass="panel"id="accordion_flowers_panel_rose_panel"style="display: block"><h3>
Some info about rose
</h3><p>
Bla bla bla... all about rose...
</p><p><imgalt="A beautiful rose"src="..." /></p><p>
A link to a page with <ahref="#">more infos about rose</a>!
</p><h3>
More elements can come here related to rose
</h3><p>
Maybe even some form element where you can enter a name for your rose: <inputtype="text" /></p></div><divclass="control"><inputclass="visually-hidden"id="accordion_flowers_panel_tulip"name="accordion_flowers"type="radio"value="accordion_flowers_panel_tulip" /><h2><labelfor="accordion_flowers_panel_tulip"><spanclass="visually-hidden">Show panel </span>tulip</label></h2></div><divclass="panel"id="accordion_flowers_panel_tulip_panel"style="display: none"><h3>
Some info about tulip
</h3><p>
Bla bla bla... all about tulip...
</p><p><imgalt="A beautiful tulip"src="..." /></p><p>
A link to a page with <ahref="#">more infos about tulip</a>!
</p><h3>
More elements can come here related to tulip
</h3><p>
Maybe even some form element where you can enter a name for your tulip: <inputtype="text" /></p></div><divclass="control"><inputclass="visually-hidden"id="accordion_flowers_panel_sunflower"name="accordion_flowers"type="radio"value="accordion_flowers_panel_sunflower" /><h2><labelfor="accordion_flowers_panel_sunflower"><spanclass="visually-hidden">Show panel </span>sunflower</label></h2></div><divclass="panel"id="accordion_flowers_panel_sunflower_panel"style="display: none"><h3>
Some info about sunflower
</h3><p>
Bla bla bla... all about sunflower...
</p><p><imgalt="A beautiful sunflower"src="..." /></p><p>
A link to a page with <ahref="#">more infos about sunflower</a>!
</p><h3>
More elements can come here related to sunflower
</h3><p>
Maybe even some form element where you can enter a name for your sunflower: <inputtype="text" /></p></div></div><p><button>Focusable element after</button></p>
Using .accordion:focus-within .control label, a style can be applied to all radio button labels upon interacting with the accordion.
This gives users a clue that they are interacting with a single control now (indicating to use the Arrow keys instead of Tab to navigate through accordion items).
If you would rather like to make each control focusable on its own, you could use a group of checkboxes instead of radio buttons.
- Do not forget to make sure only one of them is checked at a time though (using some JavaScript).
Checkboxes implementation
Based on the tablists' proof of concept, with a slightly different layout:
<p><button>Focusable element before</button></p><divclass="accordion"><h1class="visually-hidden">
flowers (accordion)
</h1><pclass="visually-hidden">
Tablist help: use the accordion controls to toggle the visibility of their respective panels (below the controls).
</p><divclass="control"><inputchecked="1"class="visually-hidden"id="accordion_flowers_panel_rose"type="checkbox" /><h2><labelfor="accordion_flowers_panel_rose"><spanclass="visually-hidden">Show panel </span>rose</label></h2></div><divclass="panel"id="accordion_flowers_panel_rose_panel"><h3>
Some info about rose
</h3><p>
Bla bla bla... all about rose...
</p><p><imgalt="A beautiful rose"src="..." /></p><p>
A link to a page with <ahref="#">more infos about rose</a>!
</p><h3>
More elements can come here related to rose
</h3><p>
Maybe even some form element where you can enter a name for your rose: <inputtype="text" /></p></div><divclass="control"><inputchecked="1"class="visually-hidden"id="accordion_flowers_panel_tulip"type="checkbox" /><h2><labelfor="accordion_flowers_panel_tulip"><spanclass="visually-hidden">Show panel </span>tulip</label></h2></div><divclass="panel"id="accordion_flowers_panel_tulip_panel"><h3>
Some info about tulip
</h3><p>
Bla bla bla... all about tulip...
</p><p><imgalt="A beautiful tulip"src="..." /></p><p>
A link to a page with <ahref="#">more infos about tulip</a>!
</p><h3>
More elements can come here related to tulip
</h3><p>
Maybe even some form element where you can enter a name for your tulip: <inputtype="text" /></p></div><divclass="control"><inputchecked="1"class="visually-hidden"id="accordion_flowers_panel_sunflower"type="checkbox" /><h2><labelfor="accordion_flowers_panel_sunflower"><spanclass="visually-hidden">Show panel </span>sunflower</label></h2></div><divclass="panel"id="accordion_flowers_panel_sunflower_panel"><h3>
Some info about sunflower
</h3><p>
Bla bla bla... all about sunflower...
</p><p><imgalt="A beautiful sunflower"src="..." /></p><p>
A link to a page with <ahref="#">more infos about sunflower</a>!
</p><h3>
More elements can come here related to sunflower
</h3><p>
Maybe even some form element where you can enter a name for your sunflower: <inputtype="text" /></p></div></div><p><button>Focusable element after</button></p>
;(function () {
$(document).ready(function () {
return $("input[type='checkbox']").each((i, element) => {
var $checkbox
$checkbox = $(element)
// Make Enter select/deselect checkbox (instead of submitting form)
$checkbox.keypress(function (e) {
if ((e.keyCode ? e.keyCode : e.which) === 13) {
return $(this).trigger('click')
}
})
return $checkbox.change(function () {
var $panel, panel_id
panel_id = `#${$checkbox.attr('id')}_panel`
$panel = $(panel_id)
if ($checkbox.is(':checked')) {
return $panel.show()
} else {
return $panel.hide()
}
})
})
})
}.call(this))
Category
Result
Comments
Date
Keyboard only
✔ (pass) pass
-
2018-5-29
NVDA 2018.1 + FF Quantum 59.0.2
✔ (pass) pass
-
2018-5-2
JAWS 2018.3 + IE 11
✔ (pass) pass
-
2018-5-23
JAWS 2018.3 + FF ESR 52.7.3
✔ (pass) pass
-
2018-5-22
Implementation details
Some interesting peculiarities:
Checkboxes replace the radio buttons to offer multiple selection.
We waived using a <fieldset>/<legend> structure, as this is no traditional group of checkboxes, and JAWS tends to be very wordy with focusable items nested within those, see Grouping form controls with fieldset and legend.
By default, only the Space key is used to toggle a checkbox (while pressing Enter submits a form).
To make it more intuitive for visual users (who do not know about any checkbox behind the scenes, and thinking they are interacting with a link or button), the Enter key was re-wired to also toggle the checkboxes.
In contrast to the radio button solution above, we omitted a visual .accordion:focus-within .control label state for the accordion items, as checkboxes are individual controls and (thereby accessed by the Tab key, as most users would expect).
Simple ARIA implementation
Instead of using radio buttons or checkboxes, it is relatively simple to create a custom accordion implementation with ARIA:
<button>Focusable element before</button><divdata-adg-accordion="true"><h2data-adg-accordion-target="first_entry">
This is the first accordion entry
</h2><divid="first_entry"><p>
This is some content of the first entry.
</p><p>
And some <ahref="#">link</a>! Nice, isn't it?
</p></div><h2data-adg-accordion-target="second_entry">
This is the second accordion entry
</h2><divid="second_entry"><p>
This is some cocntent of the second entry.
</p><p><labelfor="some_input">Some input: </label><inputid="some_input"type="text" /></p><p>
And finally a last paragraph.
</p></div></div><button>Focusable element after</button>
A link with an aria-expanded="true" attribute is placed around each panel's header; its value (true/false) and the visibility of the corresponding panel is toggled using JavaScript. See Marking elements expandable using aria-expanded.
While this may feel tempting in some circumstances, there are several drawbacks:
It needs more JavaScript (instead of relying on browser standard behaviour).
The current implementation allows multiple elements to be open. If you wanted to restrict it to one element though, a lot of additional JavaScript would be needed to manage states - something that radio buttons would offer "for free".
This solution is less intuitive: a screen reader announcement "link X collapsed" is less expressive than "show panel X checkbox not checked" or "show panel X radio button not checked 2 of 3".
Missing backwards compatibility for older clients with incomplete/missing ARIA support.