Web accessibility standards version 1
This blog post will focus on one of the key parts of our accessibility journey at Finder: establishing a first version of accessibility standards that our engineering team has committed to.
Recently I spoke at the SydCSS virtual meetup about how to get started with web accessibility when you have a large codebase and it feels overwhelming. You can view the video of this livestream on YouTube or view the slidedeck if you’re interested in the full story.
For the past 12 months, we have been working hard to educate ourselves, share knowledge and begin working on improvements to increase the accessibility of our website. Finder provides important financial information and it’s important to us that everyone has access to it. We’re at the very beginning of our accessibility journey and we have a long way to go, but we’ve also been trying hard to make positive change.
A key step for us in this process was to propose and implement standards for accessibility that our team needs to follow. The Web Content Accessibility Guidelines have three conformance levels: A, AA and AAA. Our long term goal is to comply with the AA level, but we quickly realised how hard it would be to go from nothing to AA.
Instead, we proposed a simplified set of standards that we’re aiming for our engineering team to get used to implementing and maintaining first. Then we plan to add more of the A and AA level requirements as we go. It’s a living document and a work in progress, but we wanted to share it in case it can help others on a similar journey. So these are our standards based on various resources across the web.
Accessibility standards
Overview
We (Finder) should care about the accessibility of our website. Our engineers, publishers and anybody else who creates, edits or maintains content and code should follow best accessibility practices on a daily basis.
Web accessibility refers to the inclusive practice of removing barriers that prevent interaction or access to websites by people with disabilities. When websites/apps are correctly designed, developed and edited, all users have equal access to information and functionality. An accessible website is critical for providing all of our users with a positive user experience, as well as for ensuring we comply with the Disability Discrimination Act 1992. It also has benefits for SEO.
This document describes best practises for development and content creation on finder.com and finder.com.au (this includes rest of world sites).
Alt text
- Provide clear text alternatives for non-text content (such as infographics, charts). Example:
<img src=”…” alt=”A bar chart showing sales over time. In July, sales for brand A surpassed sales for brand B and kept increasing throughout the year.”>
- Add a text alternative to all images. Example:
<img src=”…” alt=”Marketing professor pointing to a student’s computer screen during a lesson on alt text”>
- There are some exceptions:
- Icons: Usually used to complement text rather than replace it.
- Decorative images: Often used just because they look nice, they have no information or use other than aesthetics. - Alt text should:
- Be accurate and equivalent
- Be succinct
- Not be redundant
- Not use the phrases “image of….” or “graphic of…”
Colour contrast
- The visual presentation of text should have a contrast ratio of at least 4.5:1.
- Use a light background and dark text.
- Use a dark background and light text.
- Use a tool to check if it passes (see “Testing”).
- All text colours should conform. This includes links that change colour after being used, hover states, headings in menus and sidebars, as well as the main content on the page.
Text size
- While there is no official minimum font size for the web, it is generally agreed upon that 16px for body text is a good starting point.
- Text should also be big enough to pass contrast checks (see “Colour contrast”).
- Text should be able to be resized to 200% without loss of content or function.
Buttons and links
- A button should be focusable and should be able to be triggered using the space key as well as the enter key.
- A button should trigger an action, like opening or closing something.
- A link should be focusable and should be able to be triggered by using the enter key.
- A link will redirect the user to a new page, or a section within the same page.
Forms and labels
- Label all input fields clearly and helpfully.
- Place labels adjacent to the respective form control.
- Labels are usually positioned above or to the left of controls.
- Labels for checkboxes and radio buttons are normally on the right of the control.
- Labels should be associated programmatically with the correct form control using semantic HTML markup. The value of the for attribute must be the same as the value of the id attribute of the form control. Example:
<label for=”expire”>Expiration date (MM/YYYY): </label><input type=”text” name=”expire” id=”expire”>
- Ensure that forms can be completed using only a keyboard (standard semantic markup has this ability built in).
- Instructions should be simple (e.g. “required fields are in red and marked with a *” or “fill in the form and click submit to get in touch”). It should be clear to a user what information is required.
- Ensure the order of form elements are accessed in is logical (e.g. First name > Last name > DOB > Email, rather than First name > Email > DOB > Last name).
- Where a field needs a specific format, give an example (a date field might use “enter the date as DD/MM/YY”). Example:
<label for=”expire”>Expiration date (MM/YYYY): </label> <input type=”text” name=”expire” id=”expire”>
- Mark required fields with an icon and explain what the icon means before the form. Use aria-required set to true.
Form and label example:
<form method="post" action="post.php">
<p id="errors" role="alert" aria-atomic="true"></p> // JavaScript will add the errors to this paragraph
<p>Note: required fields are marked with a *</p>
<fieldset>
<legend>Please enter your contact details</legend>
<label for="name">*Your name:</label>
<input name="name" id="name" aria-required="true"/>
<br />
<label for="email">*E-Mail address:</label>
<input name="email" id="email" aria-required="true"/>
<br />
<label for="website">Website:</label>
<input name="website" id="website"/>
</fieldset>
<label for="message">*Please enter your message:</label>
<br />
<textarea name="message" id="message" rows="5" cols="80" aria-required="true"></textarea>
<br />
<fieldset>
<legend>I want to receive</legend>
<div>
<input type="checkbox" name="newsletter" id="check_1">
<label for="check_1">The weekly newsletter</label>
</div>
</fieldset>
<input type="submit" name="submit" value="Send message"/>
<input type="reset" name="reset" value="Reset form"/>
</form>
Errors
Forms must have error identification. Example:
<form method="post" action="post.php">
<p id="errors" role="alert" aria-atomic="true"></p> // JavaScript will add the errors to this paragraph
<p>Note: required fields are marked with a *</p>
<fieldset> ...
- Error identification should be easy to understand and be as specific as possible. Example:
<p id=”errors” role=”alert” aria-atomic=”true”>Error: An email address must contain a single @</p>
- Error identification should clearly show the field that contains the error.
Bad example:
<p id=”errors” role=”alert” aria-atomic=”true”>Error: must contain a single @</p>
Good example:
<p id=”errors” role=”alert” aria-atomic=”true”>Error: An email address must contain a single @</p>
- Error identification should provide suggestions (when known) to correct the errors. Example:
“Error: Phone number should be 10 digits”
- Error identification should not rely on colour alone to communicate the error.
- Error identification should be properly exposed to assistive technology. For example, a screen reader should announce that an error has appeared on the page, and read out what the error message says, otherwise a screen reader user may not know there is an error at all. aria-live can be used for this.
Example:
HTML
<fieldset>
<legend>Planet information</legend>
<label for="planetsSelect">Planet:</label>
<select id="planetsSelect" aria-controls="planetInfo">
<option value="">Select a planet…</option>
<option value="mercury">Mercury</option>
<option value="venus">Venus</option>
<option value="earth">Earth</option>
<option value="mars">Mars</option>
</select>
<button id="renderPlanetInfoButton">Go</button>
</fieldset>
<div role="region" id="planetInfo" aria-live="polite">
<h2 id="planetTitle">No planet selected</h2>
<p id="planetDescription">Select a planet to view its description</p>
</div>
<p><small>Information courtesy <a href="https://en.wikipedia.org/wiki/Solar_System#Inner_Solar_System">Wikipedia</a></small></p>
JavaScript
const PLANETS_INFO = {
mercury: {
title: 'Mercury',
description: 'Mercury is the smallest and innermost planet in the Solar System. It is named after the Roman deity Mercury, the messenger to the gods.'
},
venus: {
title: "Venus",
description: 'Venus is the second planet from the Sun. It is named after the Roman goddess of love and beauty.'
},
earth: {
title: "Earth",
description: 'Earth is the third planet from the Sun and the only object in the Universe known to harbor life.'
},
mars: {
title: "Mars",
description: 'Mars is the fourth planet from the Sun and the second-smallest planet in the Solar System after Mercury. In English, Mars carries a name of the Roman god of war, and is often referred to as the "Red Planet".'
}
};
function renderPlanetInfo(planet) {
const planetTitle = document.querySelector('#planetTitle');
const planetDescription = document.querySelector('#planetDescription');
if (planet in PLANETS_INFO) {
planetTitle.textContent = PLANETS_INFO[planet].title;
planetDescription.textContent = PLANETS_INFO[planet].description;
} else {
planetTitle.textContent = 'No planet selected';
planetDescription.textContent = 'Select a planet to view its description';
}
}
const renderPlanetInfoButton = document.querySelector('#renderPlanetInfoButton');
renderPlanetInfoButton.addEventListener('click', event => {
const planetsSelect = document.querySelector('#planetsSelect');
const selectedPlanet = planetsSelect.options[planetsSelect.selectedIndex].value;
renderPlanetInfo(selectedPlanet);
});
Headings
- Headings must be nested by their rank (or level). The most important heading is rank 1 (<h1>), the least important heading is rank 6 (<h6>).
- Skipping heading ranks can be confusing and should be avoided where possible: Make sure that a <h2> is not followed directly by an <h4>, for example. It is okay to skip ranks when closing subsections, for instance, an <h2> beginning a new section can follow an <h4> as it closes the previous section.
Keyboard access and visual focus
- All new functionality of the content should be operable through a keyboard interface. Best efforts should be taken to improve any legacy components that are touched upon as well.
- All event handlers triggered by non-keyboard UI events must also be associated with a keyboard-based event. Example:
<script>
function doStuff()
{
//do stuff
return false;
}
function keyHandler(event)
{
switch (event.which) {
case KEY_SPACE: {
event.stopPropagation;
return doStuff();
break;
}
} //end switch
return true;
}
</script>
<a role="button" href="#"
onclick="return doStuff();"
onkeypress="return keyHandler(e);">
do stuff
</a>
- All elements should have a clear visual focus style. Never remove an element’s focus state.
Moving, flashing or blinking content
- No component of the content should flash more than three times in any one-second period.
- Any moving content must have the ability to be stopped completely by the user.
- CSS animations should make use of the “prefers-reduced-motion” CSS Media Query.
Meaning for non-standard interactive elements
- Use native HTML elements where possible, as they have built in accessibility (e.g. don’t use <div role=”navigation”> when you can use <nav>).
- All non-standard interactive elements should be provided with additional information on function and state with the use of WAI-ARIA (e.g. role=”navigation” or “aria-expanded=”true”).
- aria-label should be used for any links without text (e.g. they have an svg instead). Example:
<a href="https://www.facebook.com/Findercomau/" aria-label="Facebook" class="js-track-click" data-track-category="Footer" data-track-action="Social" data-track-label="Facebook" target="_blank" rel="noopener">
<svg ...></svg>
</a>
Testing
- Colour contrast should be checked with both in-browser tools/plugins as well as the WebAIM contrast checker (believed to be the most accurate). In-browser tools/plugins could include:
- Chrome’s built in contrast checker
- Firefox’s built in contrast checker
- Are My Colours Accessible
- WCAG Color contrast checker - All features should be manually tested with a screen reader and keyboard only. At minimum, we should test on at least one of the following (based on what you have available to you — preferably both):
- NVDA on Firefox and Chrome
- VoiceOver on Safari and Chrome - Always run a lighthouse accessibility audit before deploying a new feature and fix any issues that come up (TODO: hopefully build into CI).
One key point I want to reiterate here is the importance of testing with a screen reader. Many developers believe they are improving accessibility with the use of ARIA attributes, but if we’re not testing with a screen reader how can we know that they are correct? Until you listen to it with a screen reader and ensure it makes sense, you could actually be making a component less accessible without realising. Testing with a screen reader is the equivalent of testing in the browser. We wouldn’t go live with CSS without checking if it looks okay in Chrome, Firefox, etc, so why would we go live with accessibility improvements without checking them with a screen reader? For me, this is one of the most important parts of the standards.
Since these standards were approved, all engineers at Finder are required to follow them. The benefit here is that if we review a pull request that goes against these standards, we can now block it from going live. We can request that the issues are fixed before they get to production.
We’re planning to iterate on our standards to incorporate more of the guidelines as we go, and continue to learn and share knowledge. The beginning is a really hard place to be in terms of accessibility, but we have to start somewhere.
I really hope this inspires you to look at your own processes and consider whether introducing web accessibility standards could benefit your team and your product. We also welcome any feedback so that we can improve our own processes!