-
Notifications
You must be signed in to change notification settings - Fork 363
Audit Rules
Good:
<div role="button"></div> <!-- Good: "button" is a valid ARIA role -->
<div></div> <!-- Good: No ARIA role -->
Bad:
<div role="datepicker"></div> <!-- Bad: "datepicker" is not an ARIA role -->
<div role="range"></div> <!-- Bad: "range" is an _abstract_ ARIA role -->
<div role=""></div> <!-- Bad: An empty ARIA role is not allowed -->
The ARIA roles model requires that elements with a role
attribute use a valid, non-abstract ARIA role. Each non-abstract ARIA role is mapped on to a known set of behavior by the user agent or assistive technology, so using an unknown role will result in the desired behavior not being available to the user.
You can find a list of valid ARIA roles, along with descriptions and information on additional required attributes, on the WAI-ARIA site.
Good:
<!-- Good: aria-labelledby element exists -->
<div id="label-element">
Label for text input
</div>
<input type="text" aria-labelledby="label-element"></input>
<!-- Good: aria-labelledby can refer to an element which is hidden from the page -->
<div id="hidden-label-element" style="display: none">
Hidden label
</div>
<input type="text" aria-labelledby="hidden-label-element"></input>
<!-- Good: aria-labelledby can take multiple values -->
<div id="address">Address:</div>
<span id="street">Street</span>
<input type="text" aria-labelledby="address street"></input>
<span id="city">City</span>
<input type="text" aria-labelledby="address city"></input>
Bad:
<!-- Bad: typo in aria-labelledby value -->
<div id="my-label">Label for text input</div>
<input type="text" aria-labelledby="the-label"></input>
aria-labelledby
is an attribute used to create an accessible label for any element. Its value an IDRef list, i.e. a space-separated list of element IDs, which are concatenated together to create the text alternative for the element. It can refer to any element, including the labeled element itself.
Note: The attribute is spelled
aria-labelledby
, with two 'l's. This is to maintain consistency with existing accessibility APIs. In practice, the US spellingaria-labeledby
is recognised by most browsers, but it should not be relied on.
The WAI-ARIA States and Properties spec has more information on aria-labelledby
.
Good:
<!-- Good: the checkbox role requires the aria-checked state -->
<span role="checkbox" aria-checked="false" aria-labelledby="foo" tabindex="0"></span>
Bad:
<!-- Bad: the checkbox role requires the aria-checked state -->
<span role="checkbox" aria-labelledby="foo" tabindex="0"></span>
The WAI-ARIA spec has more information on "Required States and Properties"
The values of ARIA states and properties must be of the correct type.
Bad:
<!-- Bad: the aria-hidden state is of type true/false -->
<span aria-hidden="yes">foo</span>
Good:
<!-- Good: the aria-hidden state is of type true/false -->
<span aria-hidden="true">foo</span>
The WAI-ARIA States and Properties spec has more information on attribute value types.
If the relationship is represented in the DOM, do not use aria-owns.
Bad:
<!-- Bad: the ownership is implicit in the DOM structure (each radio is a descendant of the radiogroup) -->
<ul role="radiogroup" aria-labelledby="foo" aria-owns="radio1 radio2 radio3">
<li id="radio1" tabindex="-1" role="radio" aria-checked="false">Rainbow Trout</li>
<li id="radio2" tabindex="-1" role="radio" aria-checked="false">Brook Trout</li>
<li id="radio3" tabindex="0" role="radio" aria-checked="true">Lake Trout</li>
</ul>
Good:
<!-- Good: The combobox does not own the listbox in the DOM so aria-owns is used to represent this relationship -->
<input type="text" role="combobox" aria-labelledby="foo" aria-owns="list1"/>
<ul id="list1" aria-expanded="true" role="listbox">
<li role="option" tabindex="-1">Rainbow Trout</li>
<li role="option" tabindex="-1">Brook Trout</li>
<li role="option" tabindex="-1">Lake Trout</li>
</ul>
The WAI-ARIA States and Properties spec has more information on aria-owns
.
Bad:
<!-- Bad: list1 is owned by two comboboxes -->
<input id="combo1" type="text" role="combobox" aria-labelledby="foo" aria-owns="list1"/>
<input id="combo2" type="text" role="combobox" aria-labelledby="foo" aria-owns="list1"/>
<ul id="list1" aria-expanded="true" role="listbox">
<li role="option" tabindex="-1">Rainbow Trout</li>
<li role="option" tabindex="-1">Brook Trout</li>
<li role="option" tabindex="-1">Lake Trout</li>
</ul>
The WAI-ARIA States and Properties spec has more information on aria-owns
.
Bad:
<!-- Bad: the radiogroup role must own elements with role radio -->
<ul role="radiogroup" aria-labelledby="foo">
<li id="radio1" tabindex="-1">Rainbow Trout</li>
<li id="radio2" tabindex="-1">Brook Trout</li>
<li id="radio3" tabindex="0">Lake Trout</li>
</ul>
Good:
<!-- Good: the radiogroup role must own elements with role radio -->
<ul role="radiogroup" aria-labelledby="foo">
<li id="radio1" tabindex="-1" role="radio" aria-checked="false">Rainbow Trout</li>
<li id="radio2" tabindex="-1" role="radio" aria-checked="false">Brook Trout</li>
<li id="radio3" tabindex="0" role="radio" aria-checked="true">Lake Trout</li>
</ul>
The WAI-ARIA spec has more information on "Required Owned Elements"
The required context role defines the owning container where a role is allowed.
Bad:
<!-- Bad: the listitem role must be owned by an element with role list -->
<div>
<span role="listitem">Rainbow Trout</span>
<span role="listitem">Brook Trout</span>
<span role="listitem">Lake Trout</span>
</div>
Good:
<!-- Good: the listitem role must be owned by an element with role list -->
<div role="list">
<span role="listitem">Rainbow Trout</span>
<span role="listitem">Brook Trout</span>
<span role="listitem">Lake Trout</span>
</div>
The WAI-ARIA spec has more information on "Required Context Role"
Many ARIA attributes (states and properties) can only be used on elements with particular roles.
For example aria-posinset
is only supported by listitem
, option
, menuitemradio
, radio
and
treeitem
.
Bad:
<!-- Bad: the radio role does not support the aria-required property -->
<ul role="radiogroup" aria-labelledby="foo">
<li aria-required="true" tabindex="-1" role="radio" aria-checked="false">Rainbow Trout</li>
<li aria-required="true" tabindex="-1" role="radio" aria-checked="false">Brook Trout</li>
<li aria-required="true" tabindex="0" role="radio" aria-checked="true">Lake Trout</li>
</ul>
Good:
<!-- Good: the radiogroup role does support the aria-required property -->
<ul role="radiogroup" aria-required="true" aria-labelledby="foo">
<li tabindex="-1" role="radio" aria-checked="false">Rainbow Trout</li>
<li tabindex="-1" role="radio" aria-checked="false">Brook Trout</li>
<li tabindex="0" role="radio" aria-checked="true">Lake Trout</li>
</ul>
The WAI-ARIA spec has more information on "Supported States and Properties"
This audit rule will warn you if it finds an aria-*
attribute that is not listed in the WAI-ARIA States and Properties spec.
Bad:
<!-- Bad: Labeled using incorrectly spelled aria-labeledby -->
<div id="address_label">Enter your address</div>
<input aria-labeledby="address_label">
Note: The attribute is spelled
aria-labelledby
, with two 'l's. This is to maintain consistency with existing accessibility APIs. In practice, the US spellingaria-labeledby
is recognised by most browsers, but it should not be relied on.
Good:
<!-- Good: Labeled using correctly spelled aria-labelledby -->
<div id="address_label">Enter your address</div>
<input aria-labelledby="address_label">
Some HTML elements should not be given ARIA attributes.
This is often because they are not visible, for example meta
, html
, script
, style
.
Bad:
<!-- Bad: the meta element should not be given any ARIA attributes -->
<meta charset="UTF-8" aria-hidden="false">
Good:
<!-- Good: the meta element should not be given any ARIA attributes -->
<meta charset="UTF-8">
stub
WCAG 2.0, Guideline 3.1.1 requires that
"The default human language of each Web page can be programmatically determined"
Bad:
<!-- Bad: the default human language has not been specified -->
<!DOCTYPE html>
<html>
Good:
<!-- Good: the default human language has been specified -->
<!DOCTYPE html>
<html lang="fr">
Bad:
<!-- Bad: the id 'trout' should only occur once in the "page" -->
<input type="radio" id="trout" name="trout" value="rainbow"/>
<input type="radio" id="trout" name="trout" value="brook"/>
<input type="radio" id="trout" name="trout" value="lake"/>
Good:
<!-- Good: each id is unique -->
<input type="radio" id="trout1" name="trout" value="rainbow"/>
<input type="radio" id="trout2" name="trout" value="brook"/>
<input type="radio" id="trout3" name="trout" value="lake"/>
The HTML5 spec has more information on the id
attribute.
Good:
<!-- Good: Labeled using aria-labelledby -->
<div id="address_label">Enter your address</div>
<input aria-labelledby="address_label">
<!-- Good: Labeled using aria-label -->
<input aria-label="Enter your address">
<!-- Good: Using placeholder text -->
<input placeholder="Enter your address">
<!-- Good: using label/for= -->
<label for="address">Enter your address</label>
<input id="address">
<!-- Good: using label-wrapped -->
<!-- In this case both "Address:" and the contents of the text field will be read. -->
<label>
Address:
<input>
</label>
<!-- Ok: using title attribute -->
<input title="Enter your address">
<!-- Good: video element has label -->
<video controls id="video">
<source src="video.webm" type="video/webm"/>
</video>
<label for="video">Video of ducklings</label>
Bad:
<div>
Enter your address:
<input id="address"> <!-- Bad: label not associated with control -->
</div>
<button class="enter_site"></button> <!-- Bad: button has no text description -->
Unlabelled controls cause problems for users of assistive technology, as it can be unclear what the purpose of the control is. Particularly in cases where the label is not near the control in the document order, the user may have no way of knowing what the control is for.
Assistive technology will speak the name of a form element only if it is labelled using one of the following techniques:
-
aria-labelledby
attribute -
aria-label
attribute - HTML
<label>
-
alt
attribute, for<img>
or<input type='img'>
elements -
placeholder
attribute for text boxes -
title
attribute as a last resort.
The WAI ARIA text alternative computation guide outlines what user agents should do to calculate a text alternative for an element.
Good:
<!-- Good: alt value provides alternative content for the image. -->
<img src="flowers.jpg" alt="A vase containing a dozen red roses">
<!-- Good: image used for presentation has an empty alt value -->
<img src="line.png" alt="">
<!-- Good: image used for presentation has an ARIA role of "presentation" -->
<img src="dot.png" role="presentation">
Bad:
<!-- Bad: no alternative content provided for informative image -->
<img src="stateDiagram.jpg">
<!-- Bad: presentational image may not be hidden from assistive technology -->
<img src="horizontalLine.jpg">
An img
element may contain information which is part of the page, or may be used to help create they graphic layout of the page. To an assistive technology user, the contents of the image may be inaccessible (for example, a blind user using a screenreader would not be able to see the image), so alternative content should be provided if the image contains information (such as a photograph or diagram), or it should be hidden from assistive technology if its role is purely presentational.
The image may be hidden from assistive technology by either setting an empty alt
value, or setting its role
value to presentation
.
Basic alternative content can be provided via the alt
value (also used as fallback content in many contexts, such as when the image doesn't load, or by web crawlers), and a longer description may be provided using aria-describedby
.
stub
stub
stub
High-contrast mode on many browsers causes background images to be disabled. This causes problems when background images are used to display a meaningful image, as opposed to a decorative or presentational background - for example, if the element is using the CSS sprite technique to display an icon. The result in this case would be that when high contrast mode is enabled, the icon would no longer be visible to the user. Techniques for High-Contrast-Friendly Icons gives the following before and after example:
(Before)
(After)
If possible, meaningful images should be displayed using an element with explicit image semantics, such as <img>
or <picture>
, so they remain visible in high contrast mode. (<svg>
or <canvas>
will also remain visible in high-contrast mode.) Note that sprites (an image that contains multiple images, often used for application icons) can still be used with an <img>
tag. See Techniques for High-Contrast-Friendly Icons for full details on how to achieve this.
An element which is inaccessible to a user using a mouse, but accessible to a user using the keyboard, often indicates that the element should be invisible to all users. This may be achieved by either making the element invisible using CSS display: none
or visibility: hidden
, or if the element is intended to be partially obscured but unusable then it can be hidden from assistive technology using the aria-hidden
attribute.
Note: this warning may be shown when a focusable element has zero area due to its child element having a float property. In this case, the issue is that this means that the focusable element will not have a focus ring drawn and may not be easy for screenreader users to navigate to, and it may be fixed by floating the focusable element rather than the child element. An example of this is below.
Good:
<!-- Good: focusable element has non-zero area -->
<a href="http://www.google.com" style="float: left">
<img src="http://www.google.com/images/srpr/logo11w.png"
width="269" height="95" alt="Google" >
</a>
Bad:
<!-- Bad: focusable element has zero area due to floated child element -->
<a href="http://www.google.com">
<img src="http://www.google.com/images/srpr/logo11w.png"
style="float: left" width="269" height="95" alt="Google" >
</a>
Good:
<!-- Good: span with onclick attribute is in the tab order -->
<span onclick="doSomething();" tabindex="0">Click me!</span>
<!-- Good: span with onclick attribute may be focused programmatically -->
<span onclick="doSomething();" tabindex="-1">Click me too!</span>
<!-- Good: button with click event listener set via Javascript is inherently focusable -->
<button id="button">Click me as well!</button>
<script>
document.getElementById("button").addEventListener("click", doSomething);
</script>
<!-- Good: anchor element with href is inherently focusable -->
<a href="javascript:void(0);" onclick="doSomething();">Click ALL the things!</a>
Bad:
<!-- Bad: span with onclick attribute has no tabindex -->
<span onclick="submitForm();">Submit</span>
<!-- Bad: anchor element without href is not focusable -->
<a onclick="showNextPage();">Next page</a>
Elements which have click handlers but are not focusable can not be used by keyboard-only users.
To make an element focusable, you can either set the tabindex
property, or use an element type which is inherently focusable.
tabindex
may be used in one of the following ways:
-
tabindex="0"
will put the element in the document's tab order in its 'natural' (i.e. DOM) order. - Setting a value greater than 0 for
tabindex
will put the element at that position in the document tab order. For example, settingtabindex="5"
will ensure that that element is 5th in the document tab order. Ideally, either alltabindex
values on a page should be greater than 0, or else they should all be 0, otherwise the tab order may be unpredictable. - Setting
tabindex
to a negative value will allow the element to be focused programmatically, but not place it in the tab order. This can be useful for elements which should be keyboard accessible, but not accessible via the tab order, such as a value in a pop-up menu.
Elements which are inherently focusable are:
-
<input>
,<button>
,<select>
and<textarea>
elements which are not disabled, and -
<a>
or<area>
elements with anhref
attribute.
Keyboard users move focus from one form control to the next by using the tab key. By default focus order is determined by source order. For example:
<!-- Natural tab order: focus moves from link1 to link2 to link3 -->
<a href="#one" id="link1">Link 1</a>
<a href="#two" id="link2">Link 2</a>
<a href="#three" id="link3">Link 3</a>
The tabindex
attribute allows the author to specify an alternative tab order. Elements with a tabindex
value greater than zero will receive focus in numerical order, ahead of focusable elements with a tabindex
of zero. For example:
<!-- Overridden tab order: focus order is:
1. link3
2. link2
3. link4
4. link1
5. div1
-->
<a href="#one" id="link1">Link 1 - implicit tabindex of 0</a>
<div tabindex="0" id="div1">Div 1</div>
<a tabindex="2" href="#two" id="link2">Link 2</a>
<a tabindex="1" href="#three" id="link3">Link 3</a>
<a tabindex="2" href="#four" id="link4">Link 4</a>
(Try this out.)
It is recommended that authors avoid positive values for the tabindex
attribute because it is brittle (any focusable elements added to the page without an explicit tabindex
value greater than zero will come last in the tab order) and can easily result in a page which is extremely difficult to navigate, causing accessibility problems.
You can read more about using the tabindex
attribute here.
Good:
<!-- Good: unstyled text will use the user agent stylesheet, which is likely to
be high contrast by default, or else be set by the user to a preferable
scheme. -->
<p>Some text.</p>
<!-- Good: light text on a dark background with a contrast ratio > 4.5 -->
<style>
.code {
/** Contrast ratio 15.30:1 **/
color: lime;
background-color: black;
font-family: Monaco, 'Lucida Console', monospace;
}
</style>
<div class="code">static void int main()</div>
Bad:
<!-- Bad: small text with a contrast ratio of less than 4.5:1 -->
<p style="color: gray"> <!-- Contrast ratio 3.95:1 -->
Warning: this product should not be used by any minor without adult supervision.
<!-- Bad: large text with a contrast ratio of less than 3.0:1 -->
<h1 style="color: #BBB">Very subtle heading</h1> <!-- Contrast ratio 1.92:1 -->
Low-contrast ratio text is difficult to read for many users with low vision, or even users with good vision in brightly-lit situations or on low-contrast displays.
The Web Content Accessibility Guidelines 2.0 recommendation is that text has a contrast ratio of at least 4.5:1, or 3:1 for large text. An enhanced version of this recommendation specifies a minimum contrast ratio of 7:1, or 4.5:1 for large text; the Accessibility Developer Tools Audit checks the former recommendation.
To debug contrast ratio problems, or if you wish to adhere to the more stringent guidelines for contrast ratio, you can use the Accessibility sidebar pane in the Developer Tools Elements panel, which is also part of the Accessibility Developer Tools extension. If you inspect the element which has a contrast ratio problem (tip: you can choose "Reveal in Inspector" from the context menu on the element in the Audit results), the Accessibility sidebar pane will show you the calculated contrast ratio and the foreground and background color which Accessibility Developer Tools has determined for the element.
Note: the foreground and background colors are determined from the element's style and the styles of all of its parent elements. If the element is absolute-positioned, or there is an absolute-positioned element which is positioned behind the element, the contrast ratio calculation may be incorrect.
Good:
<!-- Good: Video with caption track -->
<video controls>
<source src="video.webm" type="video/webm" />
<track kind="captions" src="captions.vtt" type="text/vtt" srclang="en" label="English Captions" default />
</video>
Bad:
<!-- Bad: No accessible content -->
<video controls>
<source src="video.webm" type="video/webm" />
</video>
Some modern browsers support the <track>
element, which can be used to provide ccaption and subtitle tracks. Providing captions (as opposed to subtitles, which are in a language other than the language spoken in the video) makes the video accessible to deaf and hard-of-hearing users.
Note: support for the
<track>
element is very new, so if you need to support more than just the most up-to-date browsers, you will need to use an alternate mechanism for providing captions. Currently,<track>
elements are available Internet Explorer 10 and Chrome 18 onwards.
The headers can be either on the top row, the left column or in a grid layout. This is a requirement for Section 508 compliance: 1194.22g
Good:
<!-- Good: Table has header row -->
<table>
<tr>
<th>Header</th>
<th>Header</th>
<th>Header</th>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
</table>
<!-- Good: Table has header column -->
<table>
<tr>
<th>Header</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th>Header</th>
<td>Cell</td>
<td>Cell</td>
</tr>
</table>
<!-- Good: Table has grid layout -->
<table>
<tr>
<td>Cell</td>
<th>Header</th>
<th>Header</th>
</tr>
<tr>
<th>Header</th>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th>Header</th>
<td>Cell</td>
<td>Cell</td>
</tr>
</table>
Bad:
<!-- Bad: Table has incomplete header row -->
<table>
<tr>
<th>Header</th>
<th>Header</th>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
</table>
<!-- Good: Table has incomplete header column -->
<table>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<th>Header</th>
<td>Cell</td>
<td>Cell</td>
</tr>
</table>
<!-- Bad: Table has no headers -->
<table>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
</table>
Presentational tables cannot have headers.
Good:
<!-- Good: Table has no headers -->
<table role="presentation">
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
</table>
Bad:
<!-- Bad: Table has headers -->
<table role="presentation">
<tr>
<th>Header</th>
<th>Header</th>
<th>Header</th>
</tr>
<tr>
<td>Cell</td>
<td>Cell</td>
<td>Cell</td>
</tr>
</table>