Fixing my blog (part 6) - Accessibility scanning
Now we've got broken links sorted, we're in a better state to start accessibility testing using the Accessibility Insights Action. This is available as both a GitHub Action and an Azure DevOps extension. I'll be using the GitHub Action version.
name: Accessibility
on:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/jekyll-build-pages
with:
verbose: false
- run: |
sudo find -type f ! -regex ".*\.\(html\|svg\|gif\|css\|jpg\|png\)" -delete
name: Remove non-HTML
# https://github.com/microsoft/accessibility-insights-action
- name: Scan for accessibility issues
uses: microsoft/accessibility-insights-action@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
site-dir: ${{ github.workspace }}/_site
scan-timeout: 6000000
#max-urls: 1500
localhost-port: 12345
scan-url-relative-path: /
- name: Upload report artifact
uses: actions/upload-artifact@v2
with:
name: accessibility-reports
path: ${{ github.workspace }}/_accessibility-reports/index.html
The action can either scan a local directory or a URL. In my case, I want it to scan all the files that make up my blog. My blog content is written in Markdown (.md) files and uses the Jekyll engine to render those pages into .html. It's the latter that should be scanned for accessibility compliance.
To generate the .html files, I make use of the Jekyll-Build-Pages action. This will generate a bunch of files under the _site
directory.
My initial testing with the scanning tool revealed it was triggering on some pages I was including but had no control over (eg. Google Ads and the Disqus comments). I wanted to exclude those from the scanning, and one way to do that is to make the content conditional. eg.
{%- if jekyll.environment == "production" -%}
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-999999999" crossorigin="anonymous"></script>
{%- endif -%}
To exclude this block, I then needed to ensure that jekyll.environment
was not set to production
. I achieved this by using a local copy of the action in which I set the value of the JEKYLL_ENV
environment variable to development
. To facilitate that I need to make a few other changes which you can see on this branch.
To help focus the scanning just on the files I care about, I added an extra step to the workflow to remove any files that weren't one of .html, .svg, .gif, .jpg or .png.
Depending on how many files you have in your website, scanning can take quite.
It's probably a good idea to not set max-urls
the first time. This uses the default of 100, which will be enough to give you an idea of the kinds of problems you need to fix.
The reason for starting small is if you have a template or CSS that are used across every page, then every page will trigger errors, and your scan will take ages to finish and contains heaps of the same error(s).
Once you've resolved those common errors, then you can ramp up your max-urls
to cover all the pages on your site (if it didn't already).
Running the workflow above also produces an 'Accessibility Checks' report. You can use this to get a quick overview of the results. To drill in to the details, you should download the build artifact view the index.html
file in your browser.
Fixing the errors
The scan flagged 5 rule violations. Expanding each rule lists the URLs that exhibited the problem, plus a suggestion on how to resolve the issue. Sometime there are multiple suggestions.
color-contrast: Ensure the contrast between foreground and background colors meet WCAG 2 AA contrast ration thresholds
Example
<a href="/tag/Family.html">Family</a>
Fix the following:
-
Element has insufficient color contrast of 4.14 (foreground color: #2a7ae2, background color: #fdfdfd, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1Element has insufficient color contrast of 4.14 (foreground color: #2a7ae2, background color: #fdfdfd, font size: 12.0pt (16px), font weight: normal). Expected contrast ratio of 4.5:1
- Use foreground color: #2773d6 and the original background color: #fdfdfd to meet a contrast ratio of 4.58:1.
-
Element has insufficient color contrast of 3.77 (foreground color: #828282, background color: #fdfdfd, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1Element has insufficient color contrast of 3.77 (foreground color: #828282, background color: #fdfdfd, font size: 10.5pt (14px), font weight: normal). Expected contrast ratio of 4.5:1
- Use foreground color: #747474 and the original background color: #fdfdfd to meet a contrast ratio of 4.59:1.
These were common across all pages, so fixing these early is a quick win. I searched for the hex color (usually it was defined in one of the .scss files that generate the CSS) and replaced it with the suggested colour
link-name: Ensures links have discernible text
Fix the following:
- Element is in tab order and does not have accessible text
Fix ONE of the following:
- Element does not have text that is visible to screen readers
- aria-label attribute does not exist or is empty
- aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty
- Element has no title attribute
image-alt: Ensures <img>
elements have alternate text or a role of none or presentation
Fix ONE of the following:
- Element does not have an alt attribute
- aria-label attribute does not exist or is empty
- aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty
- Element has no title attribute
- Element's default semantics were not overridden with role="none" or role="presentation"
html-has-lang: Ensure every HTML document has a lang attribute
Fix the following:
- The
<html>
element does not have a lang attribute
This was a false positive as for some reason it was being flagged on images.
frame-title: Ensures <iframe>
and <frame>
elements have an accessible name
Fix ONE of the following:
- Element has no title attribute
- aria-label attribute does not exist or is empty
- aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty
- Element's default semantics were not overridden with role="none" or role="presentation"
This was being flagged by some older YouTube embedded player HTML.
I've raise a bug to report the problem with links to images being flagged. It feels to me like the scanner is trying to scan image URLs (which it shouldn't). Whether that's a bug in the action, or in how I'm using it, I hope to find out soon.
Categories: Accessibility, Blogging, GitHub Actions