-
Notifications
You must be signed in to change notification settings - Fork 38
Styleguide: Accessibility
This document is meant to be referenced alongside the following styleguides:
- HTML5 Doctype
- Landmarks and Roles
- Native HTML5 Elements
- Tab Order and Focus
- Using Icon Fonts
- Form field helper text
- Figures
- Hiding content
<!DOCTYPE html>
Landmarks and roles provide meaningful, semantic keys to specific sections of the page. This allows users of assistive technology to quickly jump to designated sections. These can also act as supplementary "skip navigation" mechanisms.
The header
element specifies a header for a document or section. It typically contains a heading (Level 1 to 6) for the document or section, but can also contain a table of contents, navigation, or a search field.
<header>
<!-- Stuff here -->
...
</header>
The ARIA role="banner"
is used to indicate mostly site-oriented content rather than page-specific content.
<header role="banner">
<!-- Page header -->
...
</header>
- The
header
element should be used as a container for introductory content - You can have several
header
elements in one document - You may only use a single
role="banner"
and it must be used on aheader
element. (MS: It's unlikely that a developer would be adding a role=banner anywhere unless they are creating a brand new page template and not reusing code from our standard page header. If you agree with that, I question including header in this guide at all since only one with a role=banner is going to add any benefit to SR users and edX page templates already include a header with role=banner. Personally, I almost always recommend using an aria-label on landmark regions so they can be differentiated when viewed in a list. In this case, since it is a the Page, or Main banner, I would add aria-label="Main" to the code example above.)
The nav
element represents a section of a page that links to other pages or to parts within the page: a section with navigation links. It represents its parent element (e.g., if placed within a header
, it would represent navigation for the header
).(MS: I don't think this last part is necessarily true. There are plenty of use cases where a nav element would have nothing to do with its parent.)
The ARIA role="navigation"
is used to indicate a collection of links to navigate the document or related documents. (MS: as we discussed earlier, the role of navigation is implied, so it should not be mentioned here or in the code sample below. What is important to mention here is the importance of the aria-label here. IMHO, nav should never occur without an aria-label or labelledby. )
<nav role="navigation" aria-label="Site">
<!-- Screen readers would announce this as "Site navigation" -->
<ul>
<!-- List items with links -->
...
</ul>
</nav>
Typical forms of navigation include site navigation, section navigation, page navigation, utility navigation, and footer navigation. The nav
element usually wraps, but isn't limited to, a list
element. (MS: I think this should be "...wraps an unordered list, but doesn't have to.)
- You may use any number of
nav
elements. They represent their parent element. (MS: not necessarily true, recommend taking out.) - Always use
role="navigation"
(MS: remove this) - It is recommended to include an
aria-label
that describes the navigation, but don't use the word "navigation" itself as it'll be announced twice. (MS: This should be a MUST requirement, not a recommendation. Might help to explain why it would be announced i.e. "...as screen readers will include this information when the role is announced" instead of "...itself as it'll be announced twice")
The main
element is used to indicate the content that relates directly to the central topic of the document.
<main role="main"> _(MS: again, the role is implied, so explicitly defining it here is unnecessary)_
...
</main>
Authors are advised to use ARIA role="main"
attribute on the main element until user agents implement the required role mapping. (MS: Remove this)
- There should only be one
main
element - As a result, there should also be only a single instance of
role="main"
(MS: change this to "there should only be one main element. I also like to add an aria-label of "Content" as the screen reader will announce it and list it as Main Content.)
The ARIA role="search"
is used to define a region where search functionality is located.
<div role="search">
...
</div>
- Add the attribute
aria-labelledby
to the wrapper to explicitly say what theheading
is for this section. (MS: the div may not contain any text to be referenced by the aria-labelled by and/or aheading
. I recommend an aria-label that describes the scope of the search ie. site, product, notes, etc. Add this to the code example above.)
The ARIA role="complementary"
is used to indicate content that is complementary to the main content, yet has meaning when separated from the main content.
<div role="complementary">
...
</div>
- Add the attribute
aria-labelledby
to the wrapper to explicitly say what theheading
is for this section. (MS: again, I prefer aria-label because we can't rely on every complementary region having a heading that properly identifies it, although is more likely here than in the examples above. If we are going to recommend aria-labelledby, we should give an example of how to use it i.e. IDref. I think we she explain the benefit of doing this i.e. it makes it easier for screen reader users to navigate directly to the region or browse a list of available regions on the page)
The ARIA role="contentinfo"
indicates a region that contains information about the parent document. It often contains information like copyright and privacy statements and usually attached to the footer
. (MS: should add that the role is automatically implied when the footer element is used.)
<footer>
...
</footer>
<div role="contentinfo">
<!-- Copyright, privacy, terms, etc. -->
...
</div>
- Much like
header
, you may use multiplefooter
elements per page - You should use the
role="contentinfo"
on only one, and only once per page (MS: This seems duplicative. I don't think we need to specify using it on only one footer and only once per page.) - It is permissible to not include an
aria-labelledby
attribute (for this role or for any other role) (MS: I disagree here. All landmark elements should have either an aria-label or aria-labelledby. When you do not use an explicit label of some sort, landmark roles do not contain enough info to be of much use. We had this situation on our site recently where we had 4 nav elements with no labels and they were just listed as nav, nav, nav, nav. Which nav does the screen reader user choose if they want to access the Sequence navigation?) if there is no visual label for a region and you have used one of the standard ARIA roles to identify the role of the region.
It's important to understand the native roles of elements and use them accordingly.
Some of the more common input types that are useful to know are text
, email
, tel
, and date
.
While these new types offer improved functionality for desktops, they really shine on mobile.
When adding form fields:
- Always use a
label
that identifies the field (i.e.,<label for="name">Name:</label><input id="name" />
) - If your
label
wraps theinput
, afor
attribute isn't necessary (e.g.,<label><input /></label>
)(MS: Include a text label in this example. As it is, this is not accessible) - Establish a meaningful connection between a
label
and aninput
by pointing thefor
to theid
respectively (MS: I think we want to use the word "programmatic" or "explicit" here instead of "meaningful") - The
name
attribute isn't required, but it is helpful for normal form submissions that don't leverage JavaScript - Using
aria-labelledby
will override the DOMlabel
so use it cautiously (MS: I don't think we should even say this as we are not recommending that aria-labelledby be used on form inputs. I do think we should talk about using aria-describedby to reference (by ID) any help text that is not contained within a label element.)
<input type="text">
- Desktop: standard input field
- Mobile: normal keyboard
<input type="email">
- Desktop: standard input field
- Mobile: keyboard tailored for easy email typing, including the
@
symbol
<input type="tel">
- Desktop: standard input field
- Mobile: number pad keyboard suited for entering phone numbers
<input type="date">
- Desktop: most newer browsers display a built-in datepicker
- Mobile: usually a date spinner is presented for easy date picking
A good rule of thumb is: Buttons do something; links go somewhere.
Do this:
<button>I'll do something</button>
Not this (the role here isn't necessary):
<button role="button">I'll do something</button>
Or this:
<a href="#" role="button" class="button">Link that looks like a button</a> _(MS: actually, this would announce itself to a screen reader user as a button. The only problem with this is that the default keys for links and buttons are slightly different. IDeally, you would have JS that makes the link only activate using the same Enter key, like a button)_
-
button
's are not required to appear within aform
. Using theform
element only aids with graceful degradation in instances where JavaScript isn't available. - It is recommended that you supply a
type
. The available types are:-
submit
: The button submits the form data to the server. This is the default if the attribute is not specified, or if the attribute is dynamically changed to an empty or invalid value. -
reset
: The button resets all the controls to their initial values. -
button
: The button has no default behavior. It can have client-side scripts associated with the element's events, which are triggered when the events occur.
-
The section
element defines a section in a document. It should contain a heading
. It should not be used as a stylistic wrapper as it has meaning.
<section>
<h2>This is a list of articles</h2>
<header>
<nav role="navigation" aria-label="Section"> _(MS: I think it's confusing to use "Section" here on the topic of the section element as the context for each is different. It makes the example confusing.)_
...
</nav>
</header>
<article>
<h3>Article title</h3>
</article>
...
<footer>
...
</footer>
</section>
Tab order is the order in which focus is moved throughout the document when navigating using the Tab
key. Tab order follows the DOM and moves from the top of the DOM to the bottom. It is important to maintain proper tab order so those who rely on keyboards can navigate the page or site without interruption. (MS: i think "maintain" and "proper" are not the right words to use here. The DOM maintains the order. Also, I think "logical" is more appropriate than "proper". What we should consider saying instead is "It is important ensure that the default tab order of your page presents elements in a logical order, even when manipulating the DOM with javascript. I also think it would be helpful to introduce two key techniques for controlling tab order i.e. tabindex=-1 for elements you plan on focusing on with script, and tabindex=0 for elements that are not natively focusable, focusable.)
HTML:
<div class="modal" tabindex="-1" aria-hidden="true">
...
</div>
Script:
var initiator = document.activeElement;
$('.modal').show().focus().attr('aria-hidden', 'false');
By sending focus back to the initiating control, we help maintain a natural, semantic page flow. (MS: change semantic to logical)
HTML:
<button class="close">Close</button>
Script:
$('.modal').on('click', '.close', function() {
$('.modal').hide().attr('aria-hidden', 'true');
initiator.focus();
});
(MS: It's important to note that when a modal dialog is open, subsequent tab presses should trap focus within the dialog box, and that pressing the ESC key should perform the same action as pressing the close button.)
When using icon fonts, it's important to remember to:
- use off-screen text if the icon is displayed by itself
- hide the icon from audible output (MS: with aria-hidden=true)
- never use an icon without accompanying text (MS: equivalent text is better than accompanying text)
<a href="#"><i class="icon fa-question-mark" aria-hidden="true"></i> Get help</a>
<a href="#"><i class="icon fa-close" aria-hidden="true"></i> <span class="sr">Close</span></a>
Make sure to include aria-hidden="true"
with all icons. Otherwise screen readers will try to
announce the icon which could confuse users. (MS: i would say "may" not "will" here. Its actually very rare that they will be announced, its better to eliminate all doubt. Personally, I do this so that the icon is not displayed by visual screen readers for users with cognitive disabilities.)
Sometimes it's necessary to include helper text for a form field that's displayed below the field. Since screen readers read through the DOM linearly we need to ensure the helper text is announced.
Example:
<form>
<label for="email-address">Email address:</label>
<input type="email" name="email-address" id="email-address" aria-describedby="helper-email-address">
<span id="helper-email-address">Must be a valid email address. Example: [email protected]</span>
</form>
For semantics, wrap your images in figures
. You can have more than one img
within a figure
.
Using a figcaption
is optional but if used, should add clarity to the figure
whether it's a
single image or a collection of images.
A single image with a caption:
<figure>
<img src="path/to/image.png" alt="Puppies playing in a field">
<figcaption>Samwise Gamgee enjoys watching his puppies play in the green grass</figcaption>
</figure>
Multiple images, or charts, with a collective caption:
<figure>
<img src="chart-01-apples.png" alt="46% apples" aria-describedby="chart-caption">
<img src="chart-01-oranges.png" alt="54% oranges" aria-describedby="chart-caption">
<figcaption id="chart-caption">Ratios and percentages comparing apples to oranges</figcaption>
</figure>
Note the use of aria-described
here, tying the image to the caption _(MS: I like using the word "programmatically linking" instead of "typing" here.
There are several mechanisms that can be used for hiding content. It's important that a technique be implemented that results in the desired outcome and accessibility.
These styles will hide text from all users. The text is removed from the visual flow of the page and is ignored by screen readers. Do not use this CSS if you want the content to be read by a screen reader. But DO use it for content you don't want read by screen readers.
visibility: hidden
and/or
display: none
Useful when:
- Programmatically hiding or showing content that is in the DOM but not relevant to the current task
- Loading a modal and its content
- Loading tabbed content
A fairly modern technique of using CSS to hide or clip content that does not fit into a 1
pixel visible area will essentially hide the content visibly, but still allow it to be read by modern screen readers.
position: absolute;
clip: rect(1px, 1px, 1px, 1px);
Useful when:
- Wanting to include additional/supporting/contextual information for screen readers, but hide it visually
Using CSS to move hidden elements to a position off-screen is generally accepted as the most useful and accessible method of hiding content visually.
// example: we sometimes use the .sr class instead
.hidden {
position: absolute;
left: -10000px;
top: auto;
width: 1px;
height: 1px;
overflow: hidden;
}
When it comes to using and including images in your document, where's the best place to put them? The easy answer is: If the image provides context or meaning, it should be in the DOM. A good example of this is the edX unit navigation which lets students navigate between sections within a course. The icons provide context as to which type of content to expect (video, assignment, problem, etc.) and should be included in the DOM (and therefore announced to screen readers).
If the images are purely decorative, they should be shown using CSS.
The focus ring (the blue glow or dotted line that appears on buttons, links, or anything that has focus) is extremely important to people who rely on the keyboard to navigate. It indicates where focus is! Many designers disable or downplay the focus indicator, but this severely limits the ability to use a keyboard effectively and hurts the overall user experience.
We recommend leaving the styles alone and letting the browsers determine how they are displayed. If you feel strongly against the blue glow, you may re-style the focus, but in no circumstances are you to remove it.
// example: nope
:focus {
outline: none;
}
// example: yep (but with consistent application outlines in mind)
:focus {
outline: 2px solid yellow;
}
// example: yep (use the browser default)
:focus {
}
When building your site, take care not to overdo the engineering. Going overboard with ARIA can actually make your application less accessible. Understand the native roles of the elements you're using. Consider the following:
<button role="button">Button</button>
The role
above isn't necessary because a button
already has that role.
Similarly...
<a role="button">Link-tton</a>
Using role
above changes the native role of the link. In this case, just use a button
.
Mark, can you think of any other examples of over-engineering?