I just watched a talk by Pamela Fox at North Bay Python on Automated accessibility audits. The video should be up within 24 hours.
One of the tools Pamela introduced us to was axe-core, which is a JavaScript library at the heart of a whole ecosystem of accessibility auditing tools.
I figured out how to use it to run an accessibility audit using my shot-scraper CLI tool:
shot-scraper javascript https://datasette.io "
async () => {
const axeCore = await import('https://cdn.jsdelivr.net/npm/axe-core@4.7.2/+esm');
return axeCore.default.run();
}
"
The first line loads an ESM build of axe-core
from the jsdelivr CDN. I figured out the URL for this by searching jsdelivr and finding their axe-core page.
The second line calls the .run()
method, which defaults to returning an enormous JSON object containing the results of the audit.
shot-scraper
dumps the return value of tha async()
function to standard output in my terminal.
The output started like this:
{
"testEngine": {
"name": "axe-core",
"version": "4.7.2"
},
"testRunner": {
"name": "axe"
},
"testEnvironment": {
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/115.0.5790.75 Safari/537.36",
"windowWidth": 1280,
"windowHeight": 720,
"orientationAngle": 0,
"orientationType": "landscape-primary"
},
"timestamp": "2023-07-30T18:32:39.591Z",
"url": "https://datasette.io/",
"toolOptions": {
"reporter": "v1"
},
"inapplicable": [
{
"id": "accesskeys",
"impact": null,
"tags": [
"cat.keyboard",
"best-practice"
],
That inapplicable
section goes on for a long time, but it's not actually interesting - it shows all of the audit checks that the page passed.
The most interesting section is called violations
. We can filter to just that using jq
:
shot-scraper javascript https://datasette.io "
async () => {
const axeCore = await import('https://cdn.jsdelivr.net/npm/axe-core@4.7.2/+esm');
return axeCore.default.run();
}
" | jq .violations
Which produced (for my page) an array of four objects, starting like this:
[
{
"id": "color-contrast",
"impact": "serious",
"tags": [
"cat.color",
"wcag2aa",
"wcag143",
"ACT",
"TTv5",
"TT13.c"
],
"description": "Ensures the contrast between foreground and background colors meets WCAG 2 AA minimum contrast ratio thresholds",
"help": "Elements must meet minimum color contrast ratio thresholds",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.7/color-contrast?application=axeAPI",
"nodes": [
{
"any": [
{
"id": "color-contrast",
"data": {
"fgColor": "#ffffff",
"bgColor": "#8484f4",
"contrastRatio": 3.18,
"fontSize": "10.8pt (14.4px)",
"fontWeight": "normal",
"messageKey": null,
"expectedContrastRatio": "4.5:1",
"shadowColor": null
},
"relatedNodes": [
{
"html": "<input type=\"submit\" value=\"Search\">",
"target": [
"input[type=\"submit\"]"
]
}
],
"impact": "serious",
"message": "Element has insufficient color contrast of 3.18 (foreground color: #ffffff, background color: #8484f4, font size: 10.8pt (14.4px), font weight: normal). Expected contrast ratio of 4.5:1"
}
],
I loaded these into a SQLite database using sqlite-utils:
shot-scraper javascript https://datasette.io "
async () => {
const axeCore = await import('https://cdn.jsdelivr.net/npm/axe-core@4.7.2/+esm');
return axeCore.default.run();
}
" | jq .violations \
| sqlite-utils insert /tmp/v.db violations -
Then I ran open /tmp/v.db
to open that database in Datasette Desktop.
Created 2023-07-30T11:53:18-07:00 · Edit