Performing an accessibility test

Last updated 8 May 2023.

The companion videos and information on performing an accessibility test are available from the Accessibility Testing project page.

In part one, we looked at setting up our environment for accessibility testing, including configuring the accessibility testing spreadsheet. We scoped the homepage of the test site, BoomerangTags.com, into the following eight components to make testing easier:

  1. [Site] Header
  2. [Home page] Hero
  3. [Home page] Pet tags
  4. [Home page] Ratings
  5. [Home page] Video
  6. [Home page] Business information
  7. [Site] Footer
  8. [Site] Cookies settings

The [Site] components of Header, Footer and Cookie settings appear on every page of the site and need to be tested only once. Additionally, we have a “Common Issues” component for tracking any site-wide problems affecting multiple pages, like 2.4.1: Bypass Blocks.

Explore the component

We begin our testing with the 01 [Site] Header component.

screen shot of boomerangtags.com site header with horizontal navigation, dropdown menus, a search widget with autocomplete
BoomerangTags.com homepage site header

Using a mouse, keyboard and screen reader, explore the component. We want to get an idea of what kinds of controls and widgets the header contains. We can also do a first pass for success criteria like 2.4.7: Focus Visible when we’re tabbing through the focusable elements with the keyboard. We can discover hidden menus that display on hover or widgets that expand by activating a button.

The [Site] Header contains the following content:

  • Utility navigation with two links
  • Navigation landmark with a list of 10 items with links
    • Two links have menus
    • The last link in the list opens a search widget
  • Search field with autocomplete and a button
    • Autocomplete list contains several links
  • Shopping cart link

Test the success criteria

Do a first pass through the 50 success criteria (SC) to identify which ones are applicable to the component you’re testing. For [Site] Header, we’re able to mark 21 SC as not applicable either because the content doesn’t exist (video/audio) or the content is out of scope (page level). We then did automated and manual testing of the remaining SC.

Fails (10)

  1. 1.1.1: Non-text Content
  2. 1.4.3: Contrast (Minimum)
  3. 1.4.11: Non-text Contrast
  4. 1.4.13: Content on Hover or Focus
  5. 2.1.1: Keyboard
  6. 2.4.3: Focus Order
  7. 2.4.4: Link Purpose (In Context)
  8. 2.4.7: Focus Visible
  9. 3.2.4: Consistent Identification
  10. 4.1.2: Name, Role, Value

Passes (19)

  1. 1.3.1: Info and Relationships
  2. 1.3.2: Meaningful Sequence
  3. 1.3.3: Sensory Characteristics
  4. 1.3.4: Orientation
  5. 1.4.1: Use of Color
  6. 1.4.4: Resize text
  7. 1.4.5: Images of Text
  8. 1.4.10: Reflow
  9. 1.4.12: Text Spacing
  10. 2.1.2: No Keyboard Trap
  11. 2.1.4: Character Key Shortcuts
  12. 2.3.1: Three Flashes or Below Threshold
  13. 2.4.6: Headings and Labels
  14. 2.5.2: Pointer Cancellation
  15. 2.5.3: Label in Name
  16. 3.2.1: On Focus
  17. 3.2.2: On Input
  18. 3.3.2: Labels or Instructions
  19. 4.1.1: Parsing

Conclusion

While we found failures of only 10 of the SC, we found more than 10 total issues. For 1.4.3: Contrast (Minimum); we have five different examples of foreground text that does not have at least 4.5:1 contrast with the background color. For 4.1.2: Name, Role, Value, we have four different issues.

Download the BoomerangTags.com testing spreadsheet to explore the results.

The web is inherently accessible

Originally posted 7 February 2021. Updated 13 April 2022.

It all started when I built the Accessible Web tool which removes CSS and JS from a webpage, leaving plain HTML. I built it as a sarcastic response to a developer who changed his website to require visitors to disable JavaScript before they could view the content. The reactions to this gatekeeping on Twitter were hilarious! So many angry developers didn’t get the joke that they put up similar barriers when they require that people have JavaScript enabled to view anything on their websites.

I created an About page for the project that requires the user to disable CSS to view a message about web accessibility and the importance of semantic markup. The following is a blog article version of that webpage with code examples.

Accessible Web - please disable CSS to view this website

The web is inherently accessible

Your weekly reminder that the web is accessible by default and it’s our design decisions that stop it being accessible #a11y

k.mar (@Kevmarmol_CT) on Twitter December 7, 2020

Why did you have to disable CSS to view this webpage? No reason other than a design choice that excludes sighted people.

Did you know? This is how many visitors “view” webpages already:

  • Search engines
  • Bots
  • Site crawlers
  • Analytics
  • Blind people

This webpage is fully accessible to people with screen readers and Braille displays. But many websites are not due to poor design choices that exclude some people.

What is web accessibility?

When a page is accessible, it was developed with the intention of working for as many people with disabilities as possible. A good place to start learning is the W3C’s Introduction to Web Accessibility. Find out the different ways people with disabilities interact with the web.

Web Content Accessibility Guidelines (WCAG)

WCAG is a set of success criteria for determining if a page is accessible, led by four guiding principles:

  • Perceivable: Information and user interface components must be presentable to users in ways they can perceive.
  • Operable: User interface components and navigation must be operable.
  • Understandable: Information and the operation of the user interface must be understandable.
  • Robust: Content must be robust enough that it can be interpreted by a wide variety of user agents, including assistive technologies.

These contain guidelines and a hierarchy of success criteria from Level A to Level AAA. Many accessibility laws, and current best practice, point to WCAG 2.1 Level AA compliance. There are 50 discrete success criteria to evaluate, though many are not applicable to all pages. For example, if a page doesn’t contain video, you don’t have to evaluate against success criteria for captions or audio descriptions.

See the full list of success criteria

Did you know? The WCAG guidelines were first published in 1999. Web accessibility is not a new concept but a lot of people are learning about it only now.

Semantics

So what’s the point? The point is to develop accessible pages from the bottom up, starting with semantic HTML. A whole lot of developers think they know HTML but are actually pretty sloppy about it. Many don’t think it matters if they use a link or a button, but it does. Every semantic mistake introduces accessibility issues into your code. If you’ve never really “learned” HTML, check out this beginner’s guide to writing good HTML.

By far, CSS color contrast issues are the most frequent accessibility issues I encounter, but the HTML ones are problematic too. Outlined below are the top HTML-related accessibility issues I encounter.

Headings

If you visually scan this page, you can quickly see how it is broken up into sections. That’s due to using headings or the h1-h6 elements. It’s important that every page have at least one h1 so people and search engines know what the topic of the page is. From there, cascade down to h2, h3, and so on.

Did you know? People using screen readers can navigate by headings in much the same way that sighted people can visually scan the page for items of importance.

Buttons and links

Buttons and links may seem similar but they have very different semantic uses. If you use them interchangeably, people can get confused about what a button or link is going to do when activated.

Buttons

Buttons are used for controlling actions on the page, such as a form submit button or toggle button.

It’s preferable to use a native button element instead of creating a custom one:

  • It already has the button role
  • It’s keyboard focusable
  • It’s activated with ENTER and SPACE keys

The button element can have various states depending on its purpose:

Collapsed

<button aria-expanded="false">Menu</button>

Expanded

<button aria-expanded="true">Search</button>

Not pressed

<button aria-pressed="false">Light mode</button>

Pressed

<button aria-pressed="true">Dark mode</button>

Links

Links are used for navigation, literally for linking to another page or place on the same page. When you use a link, people expect to go somewhere new when it’s activated.

It’s important that the link text properly convey the link purpose. Avoid generic text like “click here”.

Same page link

<a href="#headings">Headings</a>

New page link

<a href="/site-map/">Site map</a>

External link

<a href="https://eff.org">EFF</a>

Email link

<a href="mailto:info@eff.org">info@eff.org</a>

Images

The one thing about accessibility most people know is that images need alt text. This may seem straightforward but let’s look at three examples.

1) Alt text

Semantically speaking, every image needs an alt="" attribute. This alone will pass an automated accessibility checker. If an image is purely decorative, you can even leave the alt value empty. But if an image provides context to the content, the alt text must accurately describe the content of the image for people who cannot see it.

<img src="cats.jpg" alt="two cats on an easy chair under a blanket">
two cats on an easy chair under a blanket

2) SVG

Inline SVG doesn’t support the alt attribute. Instead, add role="img" to the svg element to identify it as an image. Give it an accessible name using the aria-label attribute on the svg.

<svg role="img" aria-label="Bar chart showing the years 2016 to 2021 as the percentage of US consumers with a streaming video service increased from 52% to 78%." xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 650 400">...</svg>
US Consumers 52% 64% 69% 74% 78% 78% 2016 2017 2018 2019 2020 2021 0 20% 40% 60% 80% 100%

Go to the Tables section for information on providing access to all data points in the graph.

3) Icons

Any time you display icons that convey meaning to the user, you must provide a text equivalent for users of assistive technology. For example, it’s common to insert an external link icon after link text that opens in a new tab or window.

Icon fonts are not announced reliably by assistive technology. First, hide the icon using the aria-hidden attribute. Then provide a text equivalent with visually-hidden text.

Exposed icon

☎ 1-800-799-SAFE

<a href="tel:1-800-799-7233">☎ 1-800-799-SAFE</a>

Hidden icon

Call 1-800-799-SAFE

<p><a href="tel:1-800-799-7233"><span class="visually-hidden">Call</span><span aria-hidden="true">☎</span> 1-800-799-SAFE</a></p>

Tables

There are many legitimate uses for tables on the web but they are often coded incorrectly. Table data cells need corresponding table headers. This allows the applicable table header to be read by a screen reader before the table data cell contents. Additionally, tables need captions to provide a description of the table to people using screen readers.

In the SVG example under Images, we looked at a svg bar chart where we provided a snapshot of the data using aria-label. We need to provide all the data in a format that assistive technology can navigate and an easy way to do that is to provide all data points in a table.

US consumers with a streaming video subscription
Year US Consumers
2016 52%
2017 64%
2018 69%
2019 74%
2020 78%
2021 78%
<table>
  <caption>US consumers with a streaming video subscription</caption>
  <thead>
    <tr>
      <th>Year</th>
      <th>US Consumers</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>2016</td>
      <td>52%</td>
    </tr>
    <tr>
      <td>2017</td>
      <td>64%</td>
    </tr>
    <tr>
      <td>2018</td>
      <td>69%</td>
    </tr>
    <tr>
      <td>2019</td>
      <td>74%</td>
    </tr>
    <tr>
      <td>2020</td>
      <td>78%</td>
    </tr>
    <tr>
      <td>2021</td>
      <td>78%</td>
    </tr>
  </tbody>
</table>

Forms

Every input needs a label. It’s really that simple. The label needs to be visible and persistent (avoid using placeholder). This helps people remember what information they’ve entered. Programmatically link each pair matching the for attribute on the label with the id attribute on the input.

This enables a couple things:

  • People can now click or tap on the label to give focus to the input. This is especially useful for checkboxes and radio buttons that often have small hit areas.
  • People using screen readers will now hear the label announced when the input is in focus.

When you have a group of related form fields, like checkboxes or radio buttons, group them with a fieldset element and provide an accessible name for the group with a legend element.

<fieldset>
  <legend>Cat's Colors</legend>
  <p><label for="black"><input type="checkbox" id="black"> Black</label></p>
  <p><label for="white"><input type="checkbox" id="white"> White</label></p>
  <p><label for="brown-tabby"><input type="checkbox" id="brown-tabby"> Brown tabby</label></p>
  <p><label for="orange-tabby"><input type="checkbox" id="orange-tabby"> Orange tabby</label></p>
  <p><label for="torbie"><input type="checkbox" id="torbie"> Torbie</label></p>
  <p><label for="gray"><input type="checkbox" id="gray"> Gray</label></p>
</fieldset>

Many modern browsers do cursory form field validation with the required attribute though error messages are not always accessible. Let assistive technology users know when a field is required by adding the aria-required="true" attribute to inputs.

<label for="email">Email<span aria-hidden="true"> *</span></label>
<input type="email" id="email" autocomplete="email" name="email" aria-required="true">

For a deeper dive into forms, check out my blog article and talk Structuring accessible forms.

In summary

  • Web accessibility is an ongoing process
  • Use semantic markup: Native HTML + ARIA
  • Headings help people navigate
  • Buttons and links do different things
  • Images need text equivalents
  • Use tables for data
  • Structure forms in accessible ways

4 web server rules you need today

This post uses Apache examples but I’ve also provided links to Nginx documentation as these two web servers account for about two-thirds of websites at the time of this post. The concepts are similar between web servers with variance in syntax and location of web server rules.

Not sure which web server your site is running? Check this web server utility.

colorful feather logo of Apache web server

What are web server rules?

Web server rules are instructions for the web server, directing it what to do with requests to your website like loading an image, redirecting a URL, denying access to content and serving error messages.

We’ll be reviewing the following web server rules:

  1. Redirect a single page request
  2. Redirect from one file path to another
  3. Turn off directory browsing
  4. Add custom error pages

Apache web server rules live in an .htaccess file located in the website’s root directory. If an .htaccess file doesn’t already exist, you can create one using a code editor and upload it to your web server. The file must start with the . character and does not have a file extension.

Web servers respond to each browser request with a code. The most common response is a 200 which means the request is successful and the web server responds by serving up the requested content like an HTML page, image or CSS file. Each rule starts on a new line.

You can see the total number of requests each webpage makes by opening developer tools in your browser (F12) and looking at the “Network” tab as you request a URL.

screen shot of the network tab in Firefox developer tools displaying 6 200 responses to content requested for a page on domain racheleditullio.com
Network tab in Firefox developer tools

Redirect a single page request

Web server Redirect rules generally respond with either 301 (permanent redirect) or 302 (temporary redirect). The following redirect rule returns a 302 response to the browser:

Redirect 302 /story-slides/index.html /talks/

Instead of loading the requested URL https://racheleditullio.com/story-slides/, the web server redirects automatically to https://racheleditullio.com/talks/.

Network tab in developer tools displaying a 302 response to domain request racheleditullio.com and the 200 response after redirect.
302 redirect from one page to another

How To Create Temporary and Permanent Redirects with Nginx

NOTE: 301 permanent redirects get cached by the browser and can be very hard to clear. Always create and test 302 redirects, changing to 301 once you’ve tested the rule is working correctly.

Redirect from one file path to another

What if you want to redirect all requests for a site or directory? When I consolidated my blog with this website, I set up the following RewriteRule rule on the old domain:

RewriteRule ^(.*)$ https://racheleditullio.com/blog/$1 [R=302,NC,L]

Requests for posts on the old domain are sent to the new-domain/blog/the-requested-file-path/.

Example: https://www.fishyux.com/blog/2016/03/twitter-adds-alt-text-authoring-for-some-users/

Network tab in developer tools displaying a 302 response to domain request www.fishyux.com and the 200 response after redirect.
302 redirect from old domain to new domain

Creating NGINX Rewrite Rules

Turn off directory browsing

Any website directory that doesn’t load a default web page may list its contents instead. Have you tried going to your images directory? You might see something like this:

a default directory listing of image files
Files in the /images directory

Add this directive to your .htaccess file and the server will block directory browsing:

Options -Indexes

The browser now returns a 403 code, access to the requested resource is forbidden.

Web page and network tab in developer tools displaying a 403 response
403 browser response

Nginx – Disable directory listing

Add custom error pages

Web servers are setup with default error pages for some response codes like 403 and 404. Give your site that finishing touch with custom error pages. Add a directive for each error code you want to customize:

ErrorDocument 404 /404/

Include the response code and the page that should load instead.

Example: https://accessibleweb.net/foo/

Web page and network tab in developer tools displaying a 404 response - page not found
Custom 404 error page on accessibleweb.net

How To Create Custom 404 Page in NGINX

Have any questions or comments? Hit me up on Twitter.