Hello i am new to accessibility and i was checking a sidebar like component with axe dev tools, i got this issue: Nested interactive controls are not announced by screen readers.
i was wondering ho i could fix it as i dont understand how else to write it in order to make it accessible heres an example of the code
<nav>
<ul role="tablist" aria-label="menu">
<li role="tab" aria-selected="true">
<a tabindex="-1">Some link</a>
</li>
<li role="tab" aria-selected="true">
<a tabindex="-1">Some link</a>
</li>
<li role="tab" aria-selected="true">
<a tabindex="-1">Some link</a>
</li>
<ul>
<nav>
A tab widget is considered an interactive element. You click on one to switch tabs in a tab navigator. Having a nested element within the tab (in your case, a link <a>) would be weird because clicking on the link might activate the link or it might activate the tab. It's confusing.
See the tab pattern at https://www.w3.org/TR/wai-aria-practices/#tabpanel and also the associated example at https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-2/tabs.html. You'll see that they use <button> elements for the tabs instead of list elements. You can use a list if you want but the element with role="tab" must be clickable (including via a keyboard) and shouldn't have a nested element that can also be clicked on.
Perhaps your links were supposed to be in the tab panel that displays after selecting on the tab selector?
Related
We have some tabs on our page with the following structure:
<ul role="tablist">
<li>
<div role="tab" tabindex="0" aria-selected="true" aria-controls="id1">First tab content...</div>
</li>
<li>
<div role="tab">Second tab content...</div>
</li>
</ul>
This gives rise to two violations when running Axe accessibility tests, namely:
<li> elements must be contained in a <ul> or <ol>, and Certain ARIA roles must contain particular children (also the converse that certain children need particular parent roles).
I understand that the first violation is due to the tablist role meaning that the <ul> is no longer seen as a <ul>. I don't understand the second violation as the spec does not enforce that elements with role="tab" are immediate children of tablist.
One possible fix, which prevents these violations, would be to move role="tab" up to the <li> elements. The problem then though is a different violation: Nested interactive controls are not announced by screen readers, due to the contained <div> being focusable presumably. Changing this to the outer <li> would require a whole load of js and css changes, so is not a simple fix.
To what extent does this really need fixing and what is the best approach?
Axe is wrong. The spec for the tab role says:
Authors MUST ensure elements with role tab are contained in, or owned by, an element with the role tablist.
Emphasis mine. Notice that is says a tab must be contained in a tablist. Contained in does not mean a direct child but rather a descendent.
This is further emphasized if you look at the next phrase, or owned by. "Owned" has a definition:
An 'owned element' is any DOM descendant of the element, any element specified as a child via aria-owns, or any DOM descendant of the owned child.
So again, as long as the tab is a descendant of a tablist (child, grandchild, great-grandchild, etc), then technically it's valid and axe is wrong.
Now, the definition of "owned" gives you a clue as to how you can work around axe's bug in case the violation bothers you. Add aria-owns to your <ul> and point it to the tab elements (your <div>s). Your original code will pass axe like this:
<ul role="tablist" aria-owns="foo1 foo2">
<li>
<div id="foo1" role="tab" tabindex="0" aria-selected="true" aria-controls="id1">First tab content...</div>
</li>
<li>
<div id="foo2" role="tab">Second tab content...</div>
</li>
</ul>
You still have the issue of an <li> not contained in a <ul>. #graham covered that part. And I also agree with #graham that you should follow the authoring practice for the tab design pattern.
The answer with your current setup is to remove the semantic meaning from the <li>.
<ul role="tablist">
<li role="none presentation">
<div role="tab" tabindex="0" aria-selected="true" aria-controls="id1">First tab content...</div>
</li>
<li role="none presentation">
<div role="tab">Second tab content...</div>
</li>
</ul>
role="none presentation" removes all semantic meaning from the <li> (so think of it like a <div> now).
It would be preferable to move everything "up one level" to keep the DOM tidy though, so a better solution might be:
<ul role="tablist">
<li role="tab" tabindex="0" aria-selected="true" aria-controls="id1">
First tab content...
</li>
<li role="tab">
Second tab content...
</li>
</ul>
This would work as you have now changed the semantic meaning of both the <ul> and the <li>, although it might need a minor adjustment to CSS.
Also Axe can sometimes complain with the first example, even though it is valid, the second example will have no complaints from automated checkers.
Alternatively you may want to look at using <button> elements to create tabs, see the tabs example on W3 on how to structure that.
It has the advantage that the click handler can be used for both mouse and keyboard interactions because you are using a <button>.
If the menu items are links, should the screen reader read it as "home, link, menu item" or "home, menu item"?
Currently the screen reader is reading it as "home, link, menu item" but I am getting mixed information about it. Some information says it should be "home, link, menu item" because we want users to know the button is a link. However, some say that it should just be announced as "home, menu item"
Here's my code:
<ul role="menu">
<li role="menuitem">
<a aria-label="Home" href="/en-us/">
<span class="">Home</span>
</a>
</li>
<li role="menuitem">
<a aria-label="Search" href="/en-us/">
<span class="">Search</span>
</a>
</li>
</ul>
I was wondering which version is correct?
If your html is correct, then don't worry about how the screen reader announces it. JAWS, NVDA, VoiceOver, Talkback, Narrator, ChromeVox, etc may all announce it slightly different and that's ok. As long as the code is correct, then you're good.
As a side note, if you use the menu roles, then you are committing to implementing arrow key navigation. That is, think of the menu role as an old school desktop app menu where you can press alt to get to the top menu (such as file, edit, view) and then left/right arrows to navigate through the menu and up/down arrows to navigate through the submenu. It's fine to do that on a web application but it makes the code more complicated. I rarely use the menu roles and just rely on basic tabbing to navigate through a list of links.
Just for anyone who stumbles across this, aria-role="menu" and aria-role="menu-item" are not for site navigation (as appears to be the case here).
They are designed for application menus (such as drop downs with commands).
For site navigation the correct (and much simpler) mark-up is to simply use the <nav> element with a nested <ul> (unless you are supporting HTML4...and even then the <ul> would probably be enough!).
Additionally there is no real need for an aria-label in the above given examples as the aria-label is the same as the programatically determinable text (a web browser can work out that the "Home" text is within the <a> element and so will present that information to a screen reader via the accessibility tree).
As such for site navigation the following is all that is needed:
<nav>
<ul>
<li>
<a href="/en-us/">
<span class="">Home</span>
</a>
</li>
<li>
<a href="/en-us/">
<span class="">Search</span>
</a>
</li>
</ul>
</nav>
Don't use WAI-ARIA unless you have explored every other option or you need to support ancient browsers!
Additional
With aria-role="menu" you don't only have to implement arrow key navigation. You also should have functionality added that ensure that:-
the menu can be closed with Esc key.
you can skip to options using letters (so d might skip to "details", the next press of d might skip to "delete" if those were menu options).
Home should jump to the start of the list
End should jump to the last item in the list
Finally
If this is indeed a drop down menu and you have implemented all the above, your mark-up isn't quite right anyway.
The <li> should have role="presentation none" (use both "presentation" and "none" as per guidance) to remove semantic meaning and the role="menu-item" should be on the anchor itself.
<ul role="menu" id="some-menu">
<li role="presentation none">
<a role="menuitem" aria-label="Home" href="/en-us/">
<span class="">Home</span>
</a>
</li>
<li role="presentation none">
<a role="menuitem" aria-label="Search" href="/en-us/">
<span class="">Search</span>
</a>
</li>
</ul>
Also don't forget that you need to associate the <button> that opened the menu with the menu itself, so you must have an ID on the role="menu", and finally don't forget that when the menu is closed you have to return focus to the button that opened it.
<button aria-haspopup="true" aria-controls="some-menu"><!--same ID as the menu above for aria-controls-->
Open Menu
</button>
I'm having a hard time with a custom dropdown menu I made. I'm trying to make it keyboard accessible, but I'm not having much luck with the listbox option. I'm following the instructions I find under WCAG pages, but I'm not having any luck. Here's one of the dropdowns on my page:
<ul class="custom__options dropdown story-selector" id="storyFilter">
<li class="custom__option selected" data-type="all" tabindex="0">All</li>
<li class="custom__option" data-type="news" tabindex="0">News and media</li>
<li class="custom__option" data-type="analysis" tabindex="0">Analysis</li>
<li class="custom__option" data-type="press" tabindex="0">Press releases</li>
</ul>
Here's a working Codepen to play with.
Your Codepen example uses WAI-ARIA attributes but have missing roles and expected keyboard behaviours.
In order to be accessible, all elements must have a minimum of a role and a name.
Your role sets a promise to the user of what kind of element the thing is and sets an expectation of how that thing will behave. Consider a role of "slider". In that single word, you've conveyed a lot of information to the user as to what that component is and how it would behave.
The name helps the user to understand the purpose of the element. The name of a volume slide would be "Volume".
Related attributes and properties are typically applied to one element, or are implied through coded relationships (using aria-labelledby for example). You would not do the following in HTML, for example:
<span alt="My image's text alternative">
<img src="image.png">
</span>
Yet I see things like this often for ARIA menubar implementations:
<li role="menuitem">
Home
</li>
There are options here, but with similar markup, the following would be more appropriate for the interactive menuitem:
<li role="none">
<button role="menuitem" aria-haspopup="true">Home</button>
</li>
It's best to use HTML elements with the closest semantics to what you are hoping to achieve, hence using the <button> element above. A lot of accessibility comes for free when you take this approach, since browsers add many (or all) of the behaviours expected by the user.
If you were to do the above using <div> and <span> elements, you need to enable appropriate keyboard focus and handle both mouse and keyboard events.
<div role="menuitem" aria-haspopup="true" tabindex="0">Home</div>
The W3C's WAI-ARIA Authoring Practices details approaches and expectations for common design patterns. For example, if you have several top-level items to choose from, you would want to use a Navigation Menubar.
Based on the Codepen example, what you are looking for is probably something like a Menu Button (or this alternative Menu Buttons tutorial), since you only requite a single dropdown menu rather than a whole menubar. This would produce something like the following:
<button aria-expanded="false" aria-haspopup="true">Story type</button>
<div role="menu">
<button role="menuitem">Home</button>
<button role="menuitem">All</button>
<button role="menuitem">News and media</button>
<button role="menuitem">Analysis</button>
<button role="menuitem">Press releases</button>
</div>
The below is my code for aria accessibility in html.It's working fine with ChromeVox but when running with JAWS all the aria-labels are read when focus is on link Menu1.But aria-label of link options should be read when focus goes on it.
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<style>
#div1{
padding : 12px;
}
</style>
</head>
<body>
<div class="container">
<ul role="tablist" class="nav nav-tabs">
<li role="tab">
<div id="div1">
<a tabindex="0" aria-label="first menuitem" class="active">Menu1
<a class="dropdown-toggle" tabindex="0" data-toggle="dropdown" aria-label="Press spacebar to use options menu" tabindex="0">options</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation"><a tabindex="0" role="menuitem">Properties</a></li>
<li role="presentation"><a tabindex="0" role="menuitem">Edit Properties</a></li>
</ul>
</a>
</div>
</li>
<li role="tab">
Menu2
</li>
</ul>
</div>
</body>
What I want is the aria-label of each link should be read only when the focus goes on particular link, not when the focus is on link Menu1, which is the current behaviour.
Any help will be appreciated.
The first issue at play here is that you have a link within a link. Not only is it invalid to nest an <a href> within an <a href> (or any interactive content), but that also means screen readers may do unexpected things when they encounter nested interactive controls. Things which cannot be considered wrong.
This means you cannot convert the second link to a <button> and expect things to be ok as you will still have nested interactive controls.
A second issue that will come into play later is any instructions to use specific keys unless those keys are mapped. For example, your instructional text says to "Press spacebar to use options menu" but that menu appears to be in a link. The space bar does not activate a link when it has focus, it will scroll the page.
The WAI-ARIA Authoring Practices 1.1 for a tab control details the keyboard controls that it will need to support.
Finally, since <a href> is already interactive content and is keyboard accessible, you do not need tabindex="0" at all. You can (and should, IMO) remove it.
Do not nest any interactive elements. If when you click on those anchor tags it redirects the user to another page than they should have a href attribute. I am assuming you are handling the redirection with some sort of click event, if that is the case, try adding
href="#"
Do not use tabindexes unless extremely necessary, adding the href attribute should be sufficient to add those links to the tab order.
I have no idea how your design looks like, but I would suggest the following approach
<ul role="tablist">
<li role="tab">
Menu1
<div id="submenu" aria-hidden="true" style="display:none">
options
</div>
</li>
</ul>
I added aria-hidden="true" style="display:none" because I am assuming you are going to add some kind of mouse-over and focus event on the menu tab that will then show the submenu div.
Recently I've been implementing ARIA into a web application and I found this question to be quite helpful in the improving the navigation parts.
After implementing this in all modules, I discovered this HTML validation error:
Attribute aria-selected not allowed on element a at this point.
Looking at the ARIA specification, I see that aria-selected is only used in roles gridcell, option, row, and tab. In my case, the role of the link is menuitem.
This is a representative sample of the HTML code:
<nav role=navigation>
<ul role=menubar>
<li role=presentation><a href='page1.php' role=menuitem>Page 1</a></li>
<li role=presentation><a href='page2.php' role=menuitem>Page 2</a></li>
<li role=presentation><a href='page3.php' role=menuitem aria-selected=true>Page 3</a></li>
<li role=presentation><a href='page4.php' role=menuitem>Page 4</a></li>
</ul>
</nav>
As you can see, this is taken on "page 3".
What is the correct ARIA role to use here?
you may also use aria-current="page" for describing current displayed page among navigation items.
I believe that aria-selected is for 'widgets' that are one-tab stop, like a set of tabs that you then arrow around to select. The selected aspect is about which one is in focus, not which page you are on.
I would check out this as a well tested example:
http://whatsock.com/tsg/Coding%20Arena/ARIA%20Menus/Horizontal%20(Internal%20Content)/demo.htm
From: http://whatsock.com/tsg/
For showing the current page I would probably use a more traditional method: Make it not a link. E.g:
<li><a href='page2.php'>Page 2</a></li>
<li><strong>Page 3</strong></li>
This also prevents people from clicking on the same-page link by accident (which I see quite often in usability testing). You can apply the same CSS to nav ul a and nav ul strong and then override the styling for the strong.
Short answer: you can use aria-current="page" or aria-current="location" to indicate the current link in a list of links.
Your pagination component could be improved in terms of accessibility (you can see this as a variation of the similar breadcrumbs pattern):
<nav aria-label="pagination">
<ol>
<li>
Page 1
</li>
<li>
Page 2
</li>
<li>
Page 3
</li>
<li>
Page 4
</li>
</ol>
</nav>
A few notes:
Use <nav> to automatically use the navigation landmark (<nav> is equivalent to <div role="navigation"> but shorter and more elegant)
Use aria-label to provide a meaningful name to the <nav> (most likely, you have more <nav> elements on the page and you should label each one accordingly).
Use to make the set of links structured. This can also help screen reader users as it will be announced as "pagination, navigation (next) list, 4 items, helping users understand how many pages there are.
Use aria-current="location"oraria-current="page"` current page of the list (this is most likely shown in a different style as the other pages, but we need to mark it for screen reader users).