Table of contents
- Testing
- Text alternatives for images
- Labels on form fields
- Headings
- Links
- Title attributes
- Keyboard accessibility
- Tables
- Hiding content in an accessible way
- Announcing changes
Have an accessibility question? Want us to check your work for accessibility issues? Post your question to the UXUI Guild. They can be reached in the #guild-uxui channel on Slack.
One of the simplest and most powerful ways to test web content for accessibility is to navigate using a keyboard, without using a mouse or touchpad. Many groups of users rely on a keyboard to navigate and several forms of assistive technology (e.g. keyboard emulators, toggle switches, voice control) work on the same principles.
VoiceOver is a screen reader that is pre-installed on Mac computers, iPhones, iPads and iPod touches. It is a popular solution, particularly with Apple users, but is not the most widely used across all platforms. Testing with one screen reader is much like testing with one browser in that will give some insight into the likely problems but will not be fully representitive.
For more information on the limitations of testing with VoiceOver see WebAIM's article on the three things you should know before using VoiceOver for testing and PowerMapper's latest assistive technology compatibility tests.
WebAIM provides a list of keyboard shortcuts for controlling VoiceOver.
Tool | Description |
---|---|
aXe for Chrome | Adds the ability to analyse a web page for accessibility problems. Appears as a separate tab in Chrome developer tools. |
tota11y | A JavaScript bookmarklet that annotates a web page to highlight accessibility features and problems. |
WAVE Chrome Extension | An evaluation tool developed by WebAIM that provides visual feedback about the accessibility of web content by injecting icons and indicators into the page. |
Colour Contrast Analyser | Available for Mac and Windows, provides a pass/fail assessment against WCAG 2.0 colour contrast success criteria as well as a simulation of certain visual conditions. |
While tools and automation can detect rudimentary accessibility problems, many more issues can only be identified by a human tester. For example, tools can identify images without alt
attributes but cannot judge whether the text alternatives that have been provided are appropriate.
A text alternative is not always necessary (see below) but every img
element must still have an alt
attribute, regardless of whether or not it holds a value. Without an alt
attribute, screen readers and other assistive technology will often default to announcing the file name, which does not provide a good user experience.
If an image is purely decorative or does not contain any more information than is already available in text, the alt
attribute should be empty. Images with empty alt
attributes are ignored by assistive technology, avoiding unnecessary noise.
In the following example, an image of a telephone is displayed next to a phone number. The heading already provides context and meaning for the number so there is no value in the image being announced as well. The image should therefore have an empty alt
attribute so that it is ignored completely.
<h2>Call our free UK Helpline</h2>
<img src="/images/telephone.png" alt="">
<p>0800 093 5478</p>
If an image is used as a link instead of text, the alt
attribute on the img
element should indicate the purpose of the link. Follow the principles described in Links.
Every form field (apart from buttons) must have a label
that describes its purpose.
Each label
should be explicitly paired with its field by a for
attribute on the label
equal to the id
on the field. Not only does this allow screen readers and other assistive technology to announce the correct label when the field receives focus, it also increases the size of the clickable area for other users.
In the example below, the "Flying from?" label has as for="location"
attribute which explicitly pairs it with the select
control that has a corresponding id="location"
attribute.
<label for="location">Flying from?</label>
<select name="location" id="location">
<option value="ABZ">Aberdeen</option>
<option value="BFS">Belfast International</option>
...
</select>
Placeholder text (a default value in the field) should not be used instead of a separate label
element. Placeholders are cleared on focus and often not announced at all by screen readers or other assistive technology.
Hidden labels
In some cases, it might be appropriate to visually hide a label. For example, where separate fields are provided for the day, month and year of a date, it is often unnecessary to display the label for each. In this situation, it is acceptable to hide the labels with CSS as long as they remain accessible to assistive technology in the HTML. See the section on Hiding content in an accessible way for an example of this.
<fieldset>
<legend>Date of birth</legend>
<label for="day" class="hidden">Day</label>
<input id="day" type="text">
<label for="month" class="hidden">Month</label>
<select id="month">
<option value="jan">January</option>
<option value="feb">February</option>
...
</select>
<label for="year" class="hidden">Year</label>
<input id="year" type="text">
</fieldset>
As well as being explicitly associated with for
and id
attributes, labels for checkboxes and radio buttons should appear after the control in the markup and be displayed to the right of the control on screen. This convention ensures compatibility with the widest range of assistive technologies, and is also consisitent with UI patterns established by Microsoft and Apple.
A mandatory field should be identified in its label, in a way that doesn't rely on vision, so that it can be identified by both signed and non-sighted users. This could be an asterisk (*) or simply say "required". Where an asterisk or other symbol is used, this should be explained at the top of the form (e.g. "fields marked with * are required") and an abbr
should be included on the symbol itself:
<label>Email address <abbr title="required field">*</abbr></label>
Mandatory fields can also be identified programmatically using ARIA's aria-required="true"
attribute and HTML5's required
attribute.
Where all fields in a form are mandatory, it is acceptable to simply say "all fields are mandatory", but probably easier (technically) to adopt the same approach throughout and mark all individual fields.
Invalid fields can be identified programmatically using ARIA's aria-invalid="true"
attribute. Note that aria-invalid
should not be set to true
before validation has taken place.
<input id="name" type="text" aria-invalid="true">
HTML heading elements (h1
to h6
) must be used to denote headings so that the structure of the page is clear.
It is not sufficient to simply make something look like a heading by, for example, increasing its font size or changing its colour; such changes are purely visual and do not convey any semantic meaning that can be interpreted by assistive technology. Equally, heading elements should not be used to apply styling to content that is not a heading.
Headings should follow a hierarchical structure and levels should not be skipped. For example, an h2
should not be followed by an h4
without an intervening h3
.
<!-- Anti-pattern, do not copy -->
<h2>Our privacy policy</h2>
<h4>What personal information do we ask for?</h4>
<p>When you have searched for prices and availability and are ready to confirm your booking, we will ask for your name, your postal address,your fax number and e-mail address.
...
</p>
Note that there are situations in which lower level headings appear before the h1
due to page layout. This is fine as long as they themselves follow a logical hierarchy.
Generic link phrases such as "click here", "more information" or "this page" should not be used because their purpose is unknown without considering the wider context (e.g. without reading the sentence). This makes it difficult for people to navigate and also has a negative impact on SEO.
<!-- Anti-pattern, do not copy -->
<a href="">Click here</a> to book airport parking now or see <a href="">more information</a> about upgrades.
Instead, link phrases should describe their own purpose:
<a href="">Book airport parking now</a> or see more <a href="">information about upgrades</a>.
The same link phrase should not be used more than once on the same page, unless those links point to the same place. Equally, avoid using different phrases to link to the same place wherever possible.
title
attributes should not be used to provide important information because they are often ignored by screen readers and other assistive technology. They are also only displayed very briefly to mouse users and not at all on touch devices.
title
attributes should also not simply repeat information that is already provided on the page. This is redundant and annoying for anyone who can access it.
<!-- Anti-pattern, do not copy -->
<a href="" title="Book airport parking now">Book airport parking now</a>
Keyboard focus should be visible at all times so that a sighted keyboard user can see which element of the page they are interacting with. By default, browsers present the :focus
state with a dotted outline
. The styling of the outline
can be modified or replaced, as long as it remains visually obvious.
Landmark roles are a way of extending the native semantics of HTML elements in order to better describe the structure of a page. They make it easier for assistive technology users to understand and navigate content.
For a demonstration of landmark roles in action, watch the video on How ARIA landmark roles help screen reader users.
The following roles are the most useful:
Role | Description |
---|---|
role="banner" |
The main header of a page, often applied to the header element. Must only be used once per page. |
role="main" |
The main content of a document, often applied to a containing div or main element. Must only be used once per page. |
role="navigation" |
A list of links serving as a navigation menu, normally used on a nav element. There are often several instances of this landmark role on a page and therefore labels should be used to differentiate them (see below). |
role="contentinfo" |
Information about the page including contact information and legal notices, usually applied to a footer element. Must only be used once per page. |
Note that the need for landmark roles will diminish as support for HTML5 elements improves but for the time being they offer an easy and unobtrusive way to increase accessibility.
When more than one landmark with the same role is used on the same page, a descriptive label should be provided using either aria-label
or aria-labelledby
so that the user can identify the purpose of each landmark.
<!-- Providing a label with the aria-label attribute -->
<nav role="navigation" aria-label="Products and services">
...
</nav>
<!-- Providing a label with the aria-labelledby attribute -->
<nav role="navigation" aria-labelledby="heading">
<h2 id="heading">Products and services</h2>
...
</nav>
In general, elements should be left in the default tab order and tabindex
attributes should not be used to change that order. However, tabindex
can be used to add or remove elements from the tab order completely.
- Set
tabindex="-1"
to remove an element from the tab cycle but keep it focusable with mouse or JavaScript. - Set
tabindex="0"
to make an element (such as adiv
or aspan
) receive focus in the tab cycle. - When an element is given focus in JavaScript rather than as a result of the user tabbing to it, visual focus styling must still be applied.
A modal dialog is a dialog that forces the user to interact with it, blocking the rest of the application. They are often used to prompt for a 'yes' or 'no' response.
- Focus should be given to the first focusable element, the primary action inside the dialog or the dialog container when it is opened.
- Focus should be trapped inside the dialog, i.e. it should not be possible for the user to tab onto elements outside of the dialog while it is open.
- When the dialog is closed, keyboard focus should be returned to its original position, or to a sensible point of continuation.
- Pressing the escape key on the keyboard should close the dialog.
- The dialog container should have a
role="dialog"
attribute and be labelled with eitheraria-label
oraria-labelledby
.
<div role="dialog" aria-labelledby="heading">
<h2 id="heading">For £40.00 more, add a steak dinner to your booking</h2>
<p>Grab yourself a delicious steak and a glass of wine at the Courtyard's popular Casterbridge Grill restaurant.</p>
<a href="">No thanks</a>
<a href="">Yes please</a>
</div>
table
elements must be used to present tabular data. Don't usediv
s or other elements to simulate a tabular layout.table
elements must not be used purely to control page layout.- Row and column headings must be in
th
elements withscope="row"
orscope="col"
attributes.
<table>
<thead>
<th scope="col">Car Park</th>
<th scope="col">Distance</th>
<th scope="col">Transfers</th>
<th scope="col">Rating</th>
<th scope="col">Price</th>
</thead>
<tr>
<td>Purple Parking - Flexible</td>
<td>2.4 miles</td>
<td>Run every 20 minutes, take 15 minutes and are included in the price</td>
<td>86%</td>
<td>£4.00 per day</td>
</tr>
<tr>
<td>Long Stay South</td>
<td>On-airport</td>
<td>Run every 10 minutes, take 5 minutes and are included in the price</td>
<td>90%</td>
<td>£4.63 per day</td>
</tr>
</table>
Providing hidden content for assistive technology
The display: none
and visibility: hidden
properties in CSS both hide content in a way that makes it inaccessible to screen readers and other assistive technology. They should therefore generally only be used if the intention is to hide the content from all users.
The most reliable way to hide content visually while keeping it accessible to assistive technology behind the scenes is to position it off the viewport.
.hidden {
position: absolute;
left: -10000px;
top: auto;
width: 1px; /* VoiceOver fails to announce elements with 0 width */
height: 1px; /* VoiceOver fails to announce elements with 0 height */
overflow: hidden;
}
If hidden content can receive keyboard focus, it should be made visible when focused. This avoids the confusing situation where keyboard focus disappears and then reappears when tabbing through a page.
The aria-hidden="true"
and role="presentation"
attributes can be used to exclude elements from the Accessibility API, hiding them from assistive technology while keeping them visible on the page. This should only be done with the aim of improving the experience for assistive technology users (e.g. to remove duplication) and only if the hidden content is provided elsewhere. Use both attributes to ensure best compatibility across user agents.
A screen reader can only focus on one element at a time and is unaware of changes happening elsewhere on the page. This is problematic in modern web applications where performing an action on one part of the page may cause changes or notifications to appear somewhere else.
WAI-ARIA Live Regions allow these changes to be exposed to screen readers so that they can be announced to the user.
Attribute | Effect |
---|---|
aria-live="polite" |
Sets an element as a live region and will announce changes at the next opportunity. |
aria-live="assertive" |
Sets an element as a live region and will interrupt the current task to notify the user of changes. |
aria-atomic="true" |
Announces the whole live region whenever it changes. |
aria-atomic="false" |
Announces only the part that has changed. |
Note that screen reader behaviour can be erratic, particularly with complex interfaces. You may need to experiment with different configurations of markup and aria-live
attributes in order to achieve the best experience.