Series Eight aims for websites we develop to meet the accessibility standard WCAG 2.1 at a minimum of AA level. Most sites won't be certified to WCAG standards unless required to, but we should aim for at least AA where possible.
This document covers a few things to watch out for and common issues you will need to deal with whilst developing a website. It is accompanied by the Testing for Accessibility document (in-progress) which explains how we can test the accessibility of our sites using automated tools and manual processes.
To see a table of contents of this document within GitHub, you can click the "list" icon in the top-left (see this guide).
We will be hosting a workshop on accessibility building and testing for developers, in addition to one for designers. When complete the recording will also be available here.
If you have any additions, good references, changes or fixes then please start a Discussion, file an Issue, or make a Pull Request. No matter the size of your change, all are welcome. This will be a living document.
Accessibility of the web is extremely important for millions of people, providing a big moral and financial incentive to ensure the sites we produce for our clients are fully accessible to all users.
If you are sighted, without disability and in perfectly good health it can be quite difficult to conceive of how an accessible web can affect people. Consider different perspectives from your own:
- A blind person using a screen reader to have the website described to them;
- An older person with reduced vision who has to increase the font size to read on screens;
- Someone who struggles with fine motor movement relying on keyboard navigation instead of a mouse;
- A person with deuteranopia — severe red-green colour blindness — who can't tell the difference between red and green;
- Someone who has had an accident resulting in both arms in a cast using voice dictation software to control their computer/phone;
- A smartphone user in bright sun struggling to see the screen thanks to the glare;
- A user with Attention deficit hyperactivity disorder, unable to focus on the site due to moving elements;
- A short-sighted user who normally uses glasses but has just broken them and needs to order a new pair. (This is a personal anecdote, you'd be amazed how many optician websites are hard to use with poor vision)
Consider some of these viewpoints and the circumstances they are in — whether temporary, situational or permanent. It can be quite easy to assume that all of the customers of our clients are in the perfect circumstances and environments whenever they use our websites, but this is far from the truth. If you're interested in more examples like these check out Empathy Prompts.
As a developer however, we are aware that this will be a lot of information to take in — and especially if you are unfamiliar with it — may be hard. The most important thing however is that while we develop websites we try our best for great accessibility, just as we would be for performance and code consistency.
We have a great development team with a diverse set of skills, many of whom have experience with accessibility. Feel free to ask individuals or the #dev-team channel within Slack if you need any advice, someone to look at your code, or help with accessibility testing. We would all be more than happy to help.
- WCAG — Web Content Accessibility Guidelines. Common standard of web accessibility used internationally since 1999. Maintained by W3C, the current latest standard is 2.2 and shall be referenced throughout this document. Many countries base their local guidelines on WCAG including the ADA (American Disabilities Act).
- Screen Reader — Although there is a lot of different accessibility tooling, screen readers are one of the most common tools and are a good benchmark for us. Screen readers are aimed at visually-impaired users that use text-to-speech to 'read' the content out loud. When I refer to screen readers, I really mean all accessibility tooling. Check out this video of how a screen reader user surfs the web.
- Semantic Elements — Elements that have intrinsic meaning like
main
,button
orh1
. These are opposed to elements without intrinsic meaning likediv
andspan
. - Certification — A process of extremely detailed and extensive testing of a site's accessibility to confirm it is 100% compliant with WCAG etc guidelines. Certification is not required nor desired for most sites as it is very time consuming, but may be needed for public sector organisations. This would likely be handled by the most experienced accessibility dev on the team or an external consultant.
Structuring your HTML in an organised way using semantic elements makes a big difference to the accessibility of the document. Browsers and accessibility tooling—including screen readers—rely on a well-structured document in order to be meaningful. HTML5 added a load of new elements that help us do this without additional code.
- Each page should have just one
main
element for the 'content' for that page. This has anid
andtabindex="-1"
so it can be focused. - Repeatable content at the top of the page is within a
header
element. This may include the logo, navigation, contact buttons. - Standalone pieces of content that make sense alone like a blog post card use an
article
element. - Repeatable content at the bottom of the page like call to action or colophon are in a
footer
element.
Explanation and example of Document Outline
A good example of a well-structured document is the following, using semantic elements. This provides screen readers with an outline of how the document is built and allows for skipping to the part they're interested in.
<body>
<header>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/">About</a></li>
</ul>
</nav>
</header>
<main>
<h1>About Us</h1>
<p>Content</p>
</main>
<footer>
Copyright 2021
</footer>
</body>
In reality a screen reader user doesn't have a single simple document outline, but this would translate into roughly the following structure for a screen reader user:
header - "Banner"
navigation
unordered list - "2 items"
link - Home
link - About
main — "Main Content"
h1 - "About Us"
footer
Screen readers in fact present multiple different outlines, including a landmark outline, heading outline and link list. By using these outlines and the structure as presented, screen reader users can get an idea of the structure of the document and skip to the part of the page that is most relevant to them, without having to listen to the entire page.
Further Reading on Page Structure and Semantics
- Main Element, Small Tweaks That Can Make a Huge Impact on Your Website’s Accessibility — Andy Bell on CSS Tricks
- Semantic Structure: Regions, Headings, and Lists — WebAIM
- Page Structure Concepts — W3 WAI Web Accessibility Tutorials
A 'Skip Link' should be one of the first elements in the body
of your page. A skip link allows for less technical screen-reader and keyboard users to 'skip' over repetitive content like the navigation to the content of the page.
To get a very rough idea for how helpful this could be to a user, open a site and read every item aloud in the header and any areas prefacing the main content of a page. For each list the reader will announce "Unordered list, X items" too. Although some advanced screen-reader users can increase the speed of the narration, this can mean it can take a matter of minutes per-page to get to the actual content for every page visited.
Here is a basic skip link implementation for reference:
<body>
<a class="skip-link" href="#main">Skip to content</a>
<main id="main">
...
.skip-link {
// Core functionality
position: absolute;
left: 50%;
z-index: 11;
transform: translateX(-50%);
&:not(:focus) {
@apply sr-only;
}
// Optional presentation
background: #000;
color: #fff;
font-weight: bold;
border-radius: .5rem;
padding: .75rem .5rem;
}
Further Reading on Skip Links
- How to Create a “Skip to Content” Link — Paul Ryan on CSS Tricks
- "Skip Navigation" Links — WebAIM
- Understanding Success Criterion 2.4.1, Bypass Blocks — W3C Understanding WCAG 2.0
- Navigation links are within a
nav
element. - Where possible use
ul
elements for lists of navigation links. - With multiple navs on a single page add unique
aria-label
attributes to make them distinct. - Do not use
role="menu"
orrole="menuitem"
for navigation. - The current page or link has
aria-current="page"
to make the current location in the site clear. - Submenus appear immediately after their parents in the DOM, for focus or clickable menus.
For collapsible menus (eg, burger menu or clickable 'dropdown'):
- Use
aria-expanded
to communicate whether the menu is open or closed. aria-controls
is used to link the trigger element and menu container.button
element is used for the trigger.- The Esc key closes the menu and re-focuses the trigger.
Further Reading on Navigation
- Menus & Menu Buttons — Inclusive Components by Heydon Pickering
- Don’t Use ARIA Menu Roles for Site Nav — Adrian Roselli
- Use
h1
–h6
tags to mark up headings and communicate page structure. - Include only one
h1
, describing the main topic of the page. - Heading levels descend from
h1
down toh6
and are always used in sequence. Never skip a heading level, as this will break the document structure. - Every unique section of content should have a heading, otherwise it will be grouped under the heading of a previous section. If the design doesn't have one you can use a
.sr-only
class to hide the title visually. - Headings should precede any related content like images in the DOM. If design requires use flex/grid
order
property to re-order.
Example of headings in the document outline
As shown in the example below, headings also contribute to the document structure:
<main>
<h1>About Us</h1>
<p>Intro Content</p>
<h2>Our Values</h2>
<h3>Work Together</h3>
<h3>Follow Through</h3>
<h2>Our Mission</h2>
<p>Lorem Ipsum</p>
</main>
main — "Main Content"
h1 — heading level 1 "About Us"
h2 — heading level 2 "Our Values"
h3 — heading level 3 "Work Together"
h3 — heading level 3 "Follow Through"
h2 — heading level 2 "Our Mission"
Heading elements as illustrated above contribute to the document outline. What's important to note is that every time you go 'down' a heading level (eg h2
to h3
) it sits underneath the previous one. This is why there are multiple heading levels within HTML, so content can be in a hierarchy with multiple titles.
This is something that is fairly instinctive to us when we view the design and see a big header with a slightly smaller one below. Crucially though, this is a strict structure based on the levels of heading we use and will have ramifications if we don't consider it when developing.
Further Reading on Headings
I know that <button>
elements can sometimes be a pain to style, and it's so easy to add a click handler to a <div>
and be done with it right? NO—DON'T DO IT!
There are only a handful of elements in HTML that the browser considers interactive so if you need something trigger-able you should use one of these elements. By doing so you'll be getting keyboard focus, activating on Enter and Space and screen reader support for free rather than having to implement these yourself.
This is all interactive HTML elements and their purposes. When implementing consider which is most appropriate for your use.
Element | Purpose |
---|---|
button |
Interactive on-page actions that aren't better suited to a different element. |
a |
Navigations to a new page or to shift focus to a new portion of the same page. |
summary |
The trigger for details /summary disclosure widgets. |
input , select , textarea |
Forms and user input. |
All interactive elements should also have accessible text using text, .sr-only
or aria-label
. This is particularly important for icon buttons or links.
Further Reading on Interactive Elements
The destination of links should be clear from the link text alone - without any context. Screen readers have access to a 'link mode' that gives a list of all links on a page without surrounding text. This would make a "Learn More" link useless, so consider putting the link on a title or provide more context in accessible text using .sr-only
or aria-label
.
Links should not open in a new tab or window. If required, it should be clear to both screen-reader and sighted users — for example using a visual icon with alt text or including "(opens in new tab)" in the link text.
Links should not wrap large amounts of content. When encountering a link, most screen readers will announce all of the link text and then announce it is a link. Rather than wrapping an entire block with an a
element to make it all clickable, implement the box link
pattern:
<!-- element that should be clickable is position relative -->
<article class="relative">
<!-- here we link just the title to be concise and that's where users expect it -->
<h3>
<a class="box-link" href="/example/">Example Box Link</a>
</h3>
<img src="..." alt="">
<p>blah blah blah...</p>
</article>
/* add a pseudo element that is position absolutely to the bounds of the 'wrapper' */
.box-link::before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
Further Reading on Links
- Link Targets and 3.2.5 — Adrian Roselli
- Block Links, Cards, Clickable Regions, Rows, Etc. — Adrian Roselli
- Links and Hypertext — WebAIM
- Understanding Success Criterion 2.4.4, Link Purpose in Context — W3 Understanding WCAG 2.1
- All form fields have an associated
label
. This should be a constantly visible label for both screen-reader and sighted users. Where not possible, consider using a 'floating label' pattern or as a last resort uses.sr-only
to provide the label for only screen-reader users. label
elements are associated with form elements using thefor
attribute. Dictation software doesn't support 'implicit' labelling, where the form element is nested within thelabel
.- Use native browser validation where possible including
required
attribute.
Error Messages:
- Error messages should be clear and descriptive.
- Dynamically added error messages should be focused on being added and have
aria-live="polite"
so they are announced to screen-readers. - Error messages that are linked to a specific form element use
aria-describedby
on the form element.
Further Reading on Forms
- Text Labels, Small Tweaks That Can Make a Huge Impact on Your Website’s Accessibility — Andy Bell on CSS Tricks
- My Priority of Methods for Labeling a Control — Adrian Roselli
- Creating Accessible Forms — WebAIM
- Usable and Accessible Form Validation and Error Recovery — WebAIM
- Success Criterion 3.3, Input Assistance — W3 WCAG 2.1
Often lists are over-used, when implementing a ul
consider whether it really adds to the experience for a screen reader. A screen reader will announce something like this:
"Unordered list, 2 items. Start of List. First item: 'Lorem ipsum'. Second item: 'Dolor sit amet'. End of List.
Now scale that up to a sites navigation that may have multiple levels and you can imagine how helpful the Skip Link paradigm can be.
Useful: A bulleted list; Navigation links; List of products/blog posts. Less useful: A carousel without a common theme; Set of links with no relation.
When using a ul
element and list-style-type: none
, add the role="list"
attribute to the ul
. This is to work around a Safari 'optimisation' where list-style-type: none
also removes the list semantics.
Further Reading on Lists
- Every interactive element should be focusable.
- Focused elements are visible on screen and accessibly.
- Every interactive element has a focus outline (via
:focus
,:focus-visible
,:focus-within
). - If you can open a modal/dialog, the content should be focused immediately. If it's a dialog that requires interaction, focus should be trapped within it. When closing the dialog, focus should return to where it was before opening.
- Don't implement infinite scrolling.
- Focus order should match the visual order going top to bottom, left to right. If focus indicators are obvious enough there can be some slight differences.
- Consider creating your own focus outline matching the site's style that is more visible than browser defaults.
Further Reading on Keyboard Navigation
- One of my favourite accessibility testing tools: The Tab Key. — Manuel Matuzovic
- Infinite scroll - Make Wordpress Accessible
- Infinite Scroll: A Bad Idea for Usability and Accessibility
All img
elements MUST have an alt
attribute — without this screen-reader may announce the URL of the image. For entirely decorative images you can leave this attribute blank (alt=""
), but it must always be present.
Writing good alternative text is quite difficult, but it is extremely important to do. We should also always provide a way to add alt text within the CMS and educate our clients on how to write good alt text.
If you have the ability to add an instruction to an alt text field, the following may suit your needs.
Users using screen readers will be read the alt text to better understand an on-page image. It should describe what is in the image, not that this is an image.
Guides on writing alt text
- How To: Write Good Alt Text — Supercool Design
- Alternative Text — WebAIM
- Writing great alt text: Emotion matters — Jake Archibald
- Video: Writing Good Alt Text — HTTP 203 Podcast with Jake Archibald & Surma
For svg
elements inline within HTML add the role="img"
attribute to declare it as an image. If the image is decorative you can then add aria-hidden="true"
, or add alternative text within aria-label
.
Further Reading on Images
- Contextually Marking up accessible images and SVGs — Scott O'Hara
- Accessible Images — WebAIM
- Understanding Success Criterion 1.1.1, Non-text Content — W3 Understanding WCAG 2.1
- Auto-playing content should be muted, some browsers wont play content unless this is the case.
- Video should not auto-play if
prefers-reduced-motion
isreduce
. - Video and audio content should always be able to be paused.
- Where possible closed captions or transcripts are available. If not possible the key content should be available in a text format.
Further Reading on Video and Audio
It is important that there is enough contrast between colours used and that colour is never solely used to convey any information. There are many different colour-related vestibular disorders but by ensuring there is enough difference in contrast and that we use other methods to convey information we don't need to test for every single one.
Designers are aware of colour contrast requirements and should consider it in their designs. If you find any instances of low contrast, or a client asks for colour changes that are inaccessible, let the design team know and they can make necessary tweaks.
WCAG 2.2 AA compliance requires:
- Normal Text — Text smaller than 24px should have a contrast ratio of 4.5:1 against the background.
- Large Text — Text that is 24px or larger should have a contrast ratio of 3:1 against the background.
- Icons — Icons should have a contrast ratio of 3:1.
- Colour shouldn't be used as the only indicator of information, consider also adding an outline, underline or movement.
You can check colour contrasts within the Chrome and Firefox devtools but you can also use tools like Tota11y to check in-browser or tools like colourcontrast.css that allow you to input foreground/background colours.
Further Reading on Colour Contrast
- Colour Contrast, Small Tweaks That Can Make a Huge Impact on Your Website’s Accessibility — Andy Bell on CSS Tricks
- Colour State Changes, Small Tweaks That Can Make a Huge Impact on Your Website’s Accessibility — Andy Bell on CSS Tricks
- Contrast and Color Accessibility — WebAIM
- Understanding Success Criterion 1.4.1, Use of Color — W3 Understanding WCAG 2.1
- Understanding Success Criterion 1.4.3, Contrast (Minimum) — W3 Understanding WCAG 2.1
When building or including custom JavaScript components like accordions, carousels or tabs, it is your responsibility as a developer to ensure the implementation remains accessible. Using native elements like button
helps, but you will often need to convey state and link together elements to make intentions clear - particularly to visually impaired users.
Making custom components accessible can be pretty tricky, so when building a custom component consider having a look around for guidance/already accessible implementations. I suggest looking up implementations within the Series Eight developer docs, referencing sources like Inclusive Components or searching online.
It's also worth testing when the component is complete rather than waiting until the site is almost finished.
WCAG provides ARIA to help with making dynamic JS components easier to understand for screen-reader users. ARIA is implemented as attributes on HTML elements starting aria-
and the role
attribute. There are a few common 'rules' for using ARIA:
- Don't use ARIA unless you have to — Native HTML will be a lot easier and better supported:
<input type="checkbox">
vs<div role="checkbox" aria-checked="true" tabindex="0">
. - Don't change native semantics unless you really, really have to — Changing element roles is generally a bad idea unless you test extensively. For a heading that's also a button
<h2><button>heading button</button></h2>
is a lot better than<h2 role="button">heading button</h2>
. - All interactive ARIA controls must be usable with a keyboard — ARIA doesn't affect keyboard functionality but the users who rely on it will expect certain keyboard controls.
- Do not use
role="presentation"
oraria-hidden="true"
on a focusable element — Users will get confused if their keyboard focus is on an element that doesn't exist to them. - All interactive elements must have an accessible name — See Interactive Elements.
Further Reading on WAI-ARIA
There are a couple accessibility-related media queries within CSS and JS that we can use to detect the user's preferences and make appropriate changes to the site. These are exposed to the user via the preferences/options of their browser or OS. As more Browser vendors take notice and OS's evolve, more queries are being added.
This media query is extremely important, and is often set by users who feel nausea or pain to uncontrolled motion on screens. It's also beneficial to users with ADHD. Examples of this would be a lot of animation, animation-on-scroll effects or parallax. But can also extend to "Back to top" links that smooth scroll the user.
You can implement it within CSS and JavaScript like so:
@media (prefers-reduced-motion: reduce) {
.element {
animation: none;
}
}
let mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
if (mediaQuery && mediaQuery.matches) {
// it's safe to run animations
}
This doesn't necessarily mean no motion, there are cases when an interface makes a lot more sense or is more usable with slight user-controlled movement. If you don't have cases like this you could consider adding an animation and transition 'nuke' to make it easier to manage:
@media (prefers-reduced-motion: reduce) {
html:focus-within {
scroll-behavior: auto;
}
*,
*::before,
*::after {
animation-duration: .01ms !important;
animation-iteration-count: 1 !important;
scroll-behavior: auto !important;
transition-duration: .01ms !important;
}
}
Further Reading on prefers-reduced-motion
- Revisiting prefers-reduced-motion, the reduced motion media query — Eric Bailey on CSS Tricks
- Reading recommendations: Animation on the web and vestibular disorders — Manuel Matuzovic
This media query reflects the Users preference for a light or dark colour scheme. Due to the amount of effort required by design and development it's unlikely you will be implementing/using this unless fixing a specific color-scheme bug or as a client request.
For a designer this will mean making and maintaining an entirely new colour scheme for the site that is light or dark. For the developer you will need to maintain two sets of colours that can be changed on the client - likely using custom properties. You would also need to implement a 'dark mode toggle' that allows users to switch between these modes independently of the browser/OS settings.
For testing or your personal use, here is a quick CSS trick for a dark mode.
body {
background: black;
}
body, img {
filter: invert(1) hue-rotate(180deg);
}
Further Reading on prefers-color-scheme
Browsers and OS' have options for font-size, allowing the user to increase the normal size of text across the interface and in webpages. This is particularly commonly used by iOS users, and is different to zooming in on a webpage with a gesture. Many users with vision impairments like presbyopia (where many peoples vision starts to deteriorate around their 40s-50s) rely on an increased text size.
We can support these changes by using relative sizing units like rem
, em
and %
rather than px
. By using relative units the interface can scale with font-sizes easily and without further development.
We should hence default to using relative units and only use absolute units like px
where required and carefully considered. A couple examples where using absolute units makes sense:
- Decorative elements — It doesn't necessarily make sense for a border-radius for example to increase with the font-size;
- Borders — A
1px
border we may want to always be 1 pixel wide no matter the font size.
It is generally a good idea to use relative units wherever possible, this applies to other properties like line height and letter spacing. Using the unit-less line height and using em
for letter spacing allows for it to scale with font size. That's useful for both simplifying the number of declarations developers need to make, and also for maintaining legibility when the user changes font size.
Further Reading on Relative Units
You should build for a minimum viewport width of 320px
. This is a commonly agreed minimum to accommodate for smaller devices like small smartphones which are far more frequent among lower-end devices and includes the first generation iPhone SE. This 320px
is also accepted to be the minimum that device makers should use to ensure maximum compatibility with existing sites - this includes devices like the Apple Watch.
Further Reading on Viewport Sizing
The user - regardless of device - should be able to zoom into the page to at least 200% whist remaining fully functional with all content visible.
<meta name="viewport">
element doesn't includeuser-scalable=no
;<meta name="viewport">
element doesn't specifymaximum-scale
with a value less than 2;- You have built your site resilient to the text size and zoom level changing.
For most cases <meta name="viewport" content="width=device-width, initial-scale=1.0">
should be all you need
Further Reading on Viewport Zooming
- Don't Disable Zoom — Adrian Roselli
- Understanding Success Criterion 1.4.4, Resize text — W3 Understanding WCAG 2.1
To ensure interactive elements (or "targets") are large enough to be triggered, try to ensure that interactive controls are at least 44 by 44 pixels in size. This assists touch screen users with their big fingers and mouse users who may not be as precise due to circumstance, motor issues or simply missing.
Links within blocks of text are exempt from this however. In other cases, adding "invisible padding" around targets that are presented as just text may be beneficial.
Further Reading on Touch Targets