đŸĒ“ Google ZX & Axe CLI

Setup

The script iterates each route, spawns axe-core via ZX, and prints accessibility violations to the terminal.

To use google/zx and @axe-core/cli you'll need to install them.

1npm i -g zx @axe-core/cli

Next, I've added a script run command inside my package.json pointing to a .mjs script file.

1"scripts": {
2 "start": "parcel serve ./src/index.html --no-cache",
3 ...
4 "axe": "zx ./src/scripts/axe/index.mjs",
5 },

The Script

The script itself is pretty small thanks to google/zx providing lightweight wrappers around child_process.

1#!/usr/bin/env zx
2
3/*
4README
5
6GitHub: https://github.com/google/zx
7
8Install (global): npm i zx -g first && @axe-core/cli -g
9
10Node.js will treat .cjs files as CommonJS modules and .mjs files as ECMAScript modules.
11It will treat .js files as whatever the default module system for the project is
12(which is CommonJS unless package.json says "type": "module",).
13*/
14
15const answer = await question(
16 `What URL would you like to run axe against?
17 [1]: localhost
18 [2]: thejoecodes
19
20 `
21);
22
23const baseUrl =
24 answer === '1' ? 'http://localhost:1234' : 'https://www.thejoecodes.com';
25
26const pageRoutes = [
27 '/articles',
28 '/updates',
29 '/about',
30 '/arcade',
31 '/articles/the-halloween-event-loop',
32 '/articles/start-using-sass-mixins',
33 '/articles/javascript-promises',
34 '/articles/vanilla-javascript',
35 '/articles/javascript-string-methods-cheat-sheet',
36 '/articles/exploring-typescript-basic-types',
37 '/articles/css-custom-properties-theme',
38 '/articles/productive-tooling',
39 '/articles/web-accessibility',
40 '/articles/rem-is-not-for-layouts',
41 '/articles/responsive-mixin',
42 '/articles/git-ssh-config',
43 '/articles/accessibility-linting',
44];
45
46const urls = pageRoutes.map((pageRoute) => `${baseUrl}${pageRoute}`);
47
48await urls.forEach((url) => $`npx axe ${url}`);
  • The $`command` uses the spawn function from the child_process package to execute a string.
  • The question() function is a wrapper around readline. You can see the additional boilerplate needed without Google ZX when using readline over in my productive tooling article.

Final Thoughts

It's fair to say I'll be re-writing my node scripts with zx by Google. It just removes so much extra boilerplate and makes writing scripts clean, fun and easy.

Was this useful?

Thanks for reading! Time for one more?

const next = themeColors[index].trim()
body.setAttribute('dark-theme-color', next)

🎨 Theme Picker

27 Oct 2021 â€ĸ 📖 15 min read â€ĸ Updated 15 Mar 2026
⚡ Lightning read

Creating a scalable theme picker with CSS custom properties.

0
0
0
SCSS CSS
<Emoji emoji="✋" label="high five" />
// <span role="img" aria-label="high five">✋</span>
<img src={src} /> // missing alt prop

✋ Accessible Emojis

06 Oct 2021 â€ĸ 📖 5 min read â€ĸ Updated 15 Mar 2026
⚡ Lightning read

Using eslint-plugin-jsx-a11y to catch accessibility issues during development.

0
0
0
ACCESSIBILITY