Accessibility Guidelines
At Zaengle, our motto is "Be Nice, Do Good". An important aspect of living up to that standard in our code is ensuring that the sites and apps we build are fully accessible to all users. The last thing we want to do as developers is code in such a way that the end result of our code is a site/app that provides a bad experience (or even impossible experience) for some of our users.
It's important to keep in mind that "normal" users (i.e., those without disabilities), are a small subset of all users. Therefore, it's essential that we keep all user "types" in mind when write code. Building sites/apps that are accessible to users with disabilities will inevitably lead to products that provide a better experience for all users, which is certainly a worthy goal. So, as developers at Zaengle, let's all strive for that goal and make the Internet a more accessible place, one site at a time.
We can do so by following the guidelines below. It's important to note that while, yes, these are things that can be changed after the fact, we should be focusing on accessibility as we build -- it shouldn't be an afterthought.
Common Mistakes to Avoid
There are several accessibility anti-patterns. The following are some common mistakes that should be avoided:
Do not use a
<div>(or other element, like a<span>) in place of a<button>.- Related to this point, the "logout" nav item should not be an
<a>element. It should be a<button>element that's styled to look like the other nav items.
- Related to this point, the "logout" nav item should not be an
Do not skip heading levels simply to use the browser's built-in styling for headings. Use headings in order and style them as needed with custom CSS.
Do not remove focus indicators entirely. Either leave the browser defaults or provide custom styles. Tailwind provides useful classes for this (e.g.,
focus:outline-none focus:shadow-outline).Avoid a
tabindexgreater than0. If an element needs to come sooner in the tab order, move it to an earlier spot in the DOM.
Buttons
Buttons should always be a
<button>element with atype(typically eithersubmitorbutton).Icon buttons, or any buttons that don't have text content, must be given an
aria-labelproperty that contains information about the button (e.g.,aria-label="Edit file"). It can also be useful to provide anaria-labelto buttons that are repeated. For example, a list of items, each item of which has aRemovebutton, can be given anaria-labelproperty ofRemove file <file_name>. This will give a screen reader user more useful context about the button.As mentioned above, do not remove focus indicators from buttons.
Examples of Accessible Code
<button
type="button"
aria-label="Download file"
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10"
/>
</svg>
</button>
<div v-for="file in files">
<span v-text="file.name">
<button
aria-label="`Remove ${file.name}`"
v-text="Remove"
>
</div>
Form Elements
Every form element must have an associated
<label>. This can be done by placing the form element inside of the<label>element or by using the<label>'sforattribute to refer to the form element'sidattribute.Even if the label shouldn't be visible to sighted users, it still must be present in the DOM and be properly associated with the form element. In such cases, the label can be hidden with CSS (Tailwind provides the
sr-onlyclass for just such a case). However, labels provide useful click targets for sighted users, so in most cases, it's best to have the label be visible to sighted users as well.As mentioned above, do not remove focus indicators from form elements.
Examples of Accessible Code
<!-- The input is inside of the label -->
<label>
<input type="checkbox">Receive promotional offers?</input>
</label>
<!-- The label's for value matches the input's id value -->
<input
id="promo"
type="checkbox"
>
<label for="promo">Receive promotional offers?</label>
<!-- The label is properly associated with the input, but hidden from sighted users -->
<label
for="search"
class="sr-only"
>
Search users
</label>
<input
id="search"
type="text"
placeholder="Search users"
>
Images
All images must have an
altattribute.Decorative images (i.e., those that do not add any information to the page's content) should be given an empty
alt(alt="").- The Zaengle logo on this site is a perfect example of a decorative image. Vuepress mistakenly gives the logo an
altof thetitleprovided in the config (i.e.,alt="Developer Guidelines"), which will result in a screen reader announcing "developer guidelines" twice in a row.
- The Zaengle logo on this site is a perfect example of a decorative image. Vuepress mistakenly gives the logo an
Example of Accessible Code
<!-- This is the accessible version of this site's logo -->
<img
src="/logo.svg"
alt=""
class="logo"
>
Links
Links should contain meaningful text (i.e., something more descriptive than "click here" or "read more").
It's common to use a logo as a home link in a site's navigation. In such cases, the link will need an
aria-labelin order to provide accessible text to a screen reader.Like with buttons, if there are multiple list items that all contain the same link (e.g., imagine the list of Zaengsters on the Zaengle site had a "Learn More" link for each Zaengster), each link should be given an
aria-labelto provide more useful context to a screen reader user (e.g.,aria-label="Learn more about Philip Zaengle").As mentioned above, do not remove focus indicators from links.
Examples of Accessible Code
<!-- This link has text that will give a screen reader user a good idea of the link's purpose. -->
<p>
We have lots of great products.
<a href="/products">View what we have in stock right now.</a>
</p>
<!-- The aria-label tells a screen reader user where the link leads. -->
<a
href="/"
aria-label="Home"
>
<img
src="/logo.svg"
alt=""
class="logo"
>
</a>
CSS
All text contrast must meet the WCAG AA standard, which requires a ratio of at least 4.5:1 for normal text and 3:1 for large text.
- Large text is classified as 18.66+
pxfor bold text and 24+pxfor non-bold text.
- Large text is classified as 18.66+
Do not use CSS to reorder HTML elements; it will result in an illogical tab order. If content has to be rearranged, change the order of the elements in the HTML.
Do not rely on color alone to convey information.
- An active link should be denoted by more than just a different color (e.g., add a bottom border and provide the
aria-current="true"attribute). - Provide error messages for form fields, rather than just outlining the form element in red.
- An active link should be denoted by more than just a different color (e.g., add a bottom border and provide the
Use relative units (
emorrem) for text size.- Text that's sized in
pxwill not update when the user updates his/her text size preferences in the browser.
- Text that's sized in
User Zooming
- When configuring the
viewportmeta tag, never setmaximum-scale=1oruser-scalable=no. Both of these settings prevent the user from zooming.
Example of Accessible Code
<meta name="viewport" content="width=device-width, initial-scale=1">
Tooling
The
eslint-plugin-vue-a11ypackage can be installed and added to the ESLint config in order to provide accessibility-related linting rules.Google Chrome and Brave Browser both come equipped with Lighthouse, which can be used to run accessibility checks. There's also a Lighthouse extension available for Firefox.
- Lighthouse won't find all issues, so manual testing is still required. But it's good at finding issues with color contrast, missing accessible names, non-semantic HTML, etc.
There are many contrast checkers available online. A reliable one is the WebAIM Contrast Checker.
You can check screen reader output on a Mac by enabling VoiceOver with
Command + fn + F5.
ESLint Config with eslint-plugin-vue-a11y
module.exports = {
extends: ['@nuxtjs', 'plugin:nuxt/recommended', 'plugin:vue-a11y/base'],
plugins: ['vue-a11y'],
}