In order to address WCAG SC: 4.1.2 Name, Role, Value (Level A) the tab interface should communicate the appropriate semantics, roles and states to users with screen readers. Likewise, in order to meet 2.1.1 Keyboard (Level A) the tab list items must have a mechanism to be navigational with the keyboard.
Suggested solution:
Add role "tablist" to <ul class="tabs tab--primary">
Add role "presentation" to the list <li class="tabs__tab">
To the a tag add role="tab" aria-selected="true" tabindex="0"
also need to add id eg id="tab1" (unique) and aria-controls="panel+#" where "+#" is a number that uniquely identifies the panel content
For the div wrapping around the content add the following to <div class="tab-pane active"...
role="tabpanel" id="panel1" aria-labelledby="tab1" aria-hidden="false" aria-expanded="true" where tab 1 in aria-labelledby="tab1" is referring to the id of the a tag that represented the tab
| Comment | File | Size | Author |
|---|---|---|---|
| #20 | layout_builder_tabs-accessibility-3135596-20.patch | 3.54 KB | mandclu |
| #14 | layout_builder_tabs-accessibility-3135596-14.patch | 4.02 KB | mandclu |
Comments
Comment #2
WebbehComment #3
mandclu commentedThe attached patch adds a number of aria attributes, and sets unique ids not only for the tab panels (as it did previously) but now also for the tabs themselves, to properly populate the aria-labelledby property on the panels.
Here's an example of what the markup looks like for me after the patch:
Comment #4
ttronslien commentedI see that you have added tabindex to the tabpanel is there a reason for this?
According to Success Criterion 2.4.7 Focus Visible, any keyboard operable user interface has a mode of operation where the keyboard focus indicator is visible. The div that displays the content for the tab is not operable, you can't interact with it using the keyboard, you interact with the tab. The purpose of the focus is to identify the active keyboard actionable element amongst many actionable elements
Making a case for adding tabindex to the content of the tab (aka tabpanel) could lead to making a case for e.g the body copy to become focus visible.
I'd recommend following the WCAG standards interpretation of using Focus Visible to identify elements that a keyboard can interact with and not follow the implementation of the example used at Aria: tab role.
Comment #5
ttronslien commentedComment #6
ttronslien commentedComment #7
mandclu commentedYes the ARIA: tab role infromation suggests the tabindex on the destination tabpanel so I added that, though I do notice that this adds a noticeable focus ring ring around the tab content. I can see where this is helpful to make it explicit what has happened, but I can also see where some clients/site owners may not consider this desirable. I will confess that I am far from an "ultimate authority" on accessibility. Perhaps we could recruit the opinion of a certain accessibility consultant we both know?
Comment #8
mandclu commentedI also just came across this WCAG-provided example where not only does the tab panel on have a tabindex value, only the active tab does: https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-2/tabs.html
On the keyboard you can use the right and left arrows to move between tabs instead. Maybe this is a better approach? Curious to know if a keyboard user would consider this a common pattern.
Comment #9
ttronslien commentedYes, the input of a keyboard user would be very valuable. The example at https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-2/tabs.html did not fall intuitive to me (I still have not figured out how to navigate between the tabs using the keyboard)
Comment #10
mandclu commentedThere's also this, which has a number of similarities (including the fact that it doesn't shift focus to the tab panel) but directly changes the selected tab based on the arrow keys: https://matthiasott.github.io/a11y-accordion-tabs/
Comment #11
mandclu commentedAdditional comments from a conversion with @andrewmachpherson in the #accessibility slack channel:
Comment #12
mandclu commentedUpdated patch, trying to draw on best practices from the examples cited in this thread. In particular:
Comment #13
mandclu commentedI got some feedback that a label to indicate the way to switch between tabs would be helpful, so that's in the attached patch.
Comment #14
mandclu commentedMissed a closing double-quote, new patch.
Comment #15
franwyllie commentedUsing macOS Catalina VoiceOver software, tab navigation aria-label instructions are read clearly to the user. Navigating tabs with the arrow keys while using VO as per the aria-label instructions works.
Comment #17
mandclu commentedThanks everyone, this has been merged in. Should have out a new release shortly.
Comment #18
andrewmacpherson commentedThe
<nav>element here has several problems:role=tablistwith<nav role=navigation>. Thenavigationrole is a landmark region, buttablistisn't. Moreover, tabs don't generally amount to navigation; it's a misuse of the role.role=regionand an appropriate label.<main>landmark). Nor does it qualify as a landmark merely because it contains some tabs.<nav>landmark region. The tablist and tabpanel roles are sufficient. The active tabpanel follows directly after the tablist (because the inactive tabpanels are hidden), so all the user needs to do to find the tablist is read backwards a little bit.'Please use the left and right arrow keys to move between tabs'isn't appropriate as anaria-label:aria-label. It's for the name of the landmark, not how to operate the controls inside.tablistitself, usingaria-describedby, and visible for all users.Recommendation: you can fix all of this at once by removing the
<nav>wrapping element. (Or you change it to a<div>, if you need to keep the classes for styling; but without aroleoraria-label.)Comment #19
andrewmacpherson commentedThe HTML source (from the twig template) contains lots of ARIA attributes which are better added using Javascript. It's a good practice to keep ARIA roles, properties, and states relating to dynamic behaviour out of the static HTML, because they are only relevant when the JS runs.
The "tabs" are actually a list of internal fragment links with overridden roles and behaviour. This is good because it can work without Javascript, and the tab behaviour is a progressive enhancement.
If JS doesn't run for some reason, the tab behaviour doesn't work. However the list and link roles are still overridden, so the presence of tabs will still be announced. This is undesirable because it's telling assistive technology users to expect behaviour which isn't there.
I recommend taking these roles and properties out of the Twig template, and initializing them in Javascript:
role=tablist(on the UL)role=presentation(on the LI)role=tab(on the links)aria-controls(on the links)aria-selected(on the links)tabindex="-1"- this is crucial to move to JS, as it's currently stopping the links from working as links when JS doesn't run.role=tabpanel(on the div.tab-pane)aria-hidden(on the div.tab-pane)aria-selected(on the div.tab-pane)aria-labelledby(on the div.tab-pane) to JS. Without aroleattribute,aria-labelledbyhas no effect on adiv.What is
data-toggle="tab"used for? It isn't mentioned in the module's Javascript.Comment #20
mandclu commented@andrewmacpherson Thanks for the very detailed feedback! The attached patch should address your feedback, and also seems to make the code more readable.
Comment #21
andrewmacpherson commentedI read patch #20 - yes, that's very much what I had in mind. Note I haven't manually tested this, just a code review.
The use of
replaceis nice - very easy to follow.Comment #23
mandclu commentedMerging this in to make an updated release. Thanks for the feedback, everyone.