Audit Rules

Antonio Devís edited this page Dec 12, 2016 · 33 revisions

ARIA

AX_ARIA_01

Elements with ARIA roles must use a valid, non-abstract ARIA role

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.

AX_ARIA_02

aria-labelledby attributes should refer to an element which exists in the DOM

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 spelling aria-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.

AX_ARIA_03

Elements with ARIA roles must have all required attributes for that role

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"

AX_ARIA_04

ARIA state and property values must be valid

The values of ARIA states and properties must be of the correct type.

Good:

<!-- Good: the aria-hidden state is of type true/false -->
<span aria-hidden="true">foo</span>

Bad:

<!-- Bad: the aria-hidden state is of type true/false -->
<span aria-hidden="yes">foo</span>

The WAI-ARIA States and Properties spec has more information on attribute value types.

AX_ARIA_05

role=main should only appear on significant elements

AX_ARIA_06

aria-owns should not be used if ownership is implicit in the DOM

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.

AX_ARIA_07

An element's ID must not be present in more than one aria-owns attribute at any time

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.

AX_ARIA_08

Elements with ARIA roles must ensure required owned elements are present

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>

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>

The WAI-ARIA spec has more information on "Required Owned Elements"

AX_ARIA_09

Elements with ARIA roles must be in the correct scope

The required context role defines the owning container where a role is allowed.

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>

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>

The WAI-ARIA spec has more information on "Required Context Role"

AX_ARIA_10

This element has an unsupported ARIA attribute

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.

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>

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>

The WAI-ARIA spec has more information on "Supported States and Properties"

AX_ARIA_11

This element has an invalid ARIA attribute

This audit rule will warn you if it finds an aria-* attribute that is not listed in the WAI-ARIA States and Properties spec.

Good:

<!-- Good: Labeled using correctly spelled aria-labelledby -->
<div id="address_label">Enter your address</div>
<input aria-labelledby="address_label">

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 spelling aria-labeledby is recognised by most browsers, but it should not be relied on.

AX_ARIA_12

This element does not support ARIA roles, states and properties

Some HTML elements should not be given ARIA attributes. This is often because they are not visible, for example meta, html, script, style.

Good:

<!-- Good: the meta element should not be given any ARIA attributes -->
<meta charset="UTF-8">

Bad:

<!-- Bad: the meta element should not be given any ARIA attributes -->
<meta charset="UTF-8" aria-hidden="false">

AX_ARIA_13

A tabpanel should be related to a tab via aria-controls or aria-labelledby

stub

Audio

AX_AUDIO_01

Audio elements should have controls

stub

HTML

AX_HTML_01

The web page should have the content's human language indicated in the markup

WCAG 2.0, Guideline 3.1.1 requires that

"The default human language of each Web page can be programmatically determined"

Good:

<!-- Good: the default human language has been specified -->
<!DOCTYPE html>
<html lang="fr">

Bad:

<!-- Bad: the default human language has not been specified -->
<!DOCTYPE html>
<html>

AX_HTML_02

An element's ID must be unique in the DOM

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"/>

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"/>

The HTML5 spec has more information on the id attribute.

Text

AX_TEXT_01

Controls and media elements should have labels

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 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 -->

<input placeholder="Enter your address">  <!-- Bad: placeholder is used in place of a label -->

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
  • 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.

AX_TEXT_02

Images should have an alt attribute, unless they have an ARIA role of "presentation"

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.

AX_TEXT_03

Labels should only contain one labelable element

stub

AX_TEXT_04

The purpose of each link should be clear from the link text

stub

Title

AX_TITLE_01

The web page should have a title that describes topic or purpose

stub

Image

AX_IMAGE_01

Meaningful images should not be used in element backgrounds

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:

a toolbar showing five different icon buttons (Before)

five black squares (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.

Focus

AX_FOCUS_01

These elements are focusable but either invisible or obscured by another element

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>

AX_FOCUS_02

Elements with onclick handlers must be focusable

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 usage

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, setting tabindex="5" will ensure that that element is 5th in the document tab order. Ideally, either all tabindex 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 an href attribute.

AX_FOCUS_03

Avoid positive integer values for tabIndex

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.

Color

AX_COLOR_01

Text elements should have a reasonable contrast ratio

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.

Video

AX_VIDEO_01

Video elements should use <track> elements to provide captions

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.

Table

AX_TABLE_01

Tables must have appropriate headers

Data Tables

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>

<!-- Bad: 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>

Tables used for layout

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>

Tooltip

AX_TOOLTIP_01

Elements with role=tooltip should have a corresponding element with aria-describedby

stub