Why Automate VoiceOver?

Automated accessibility checks usually stop at static analysis or integration tests. Tools like AccessLint and Axe catch many markup-level issues, but they never exercise the actual assistive technology a user will run. To fill that gap, Auto VO acts as a driver for the macOS VoiceOver screen reader, letting you run end-to-end checks against real VoiceOver output without needing to learn the tool's complex keyboard interface.

Auto VO is a Node module and CLI. It handles the full lifecycle of VoiceOver — launch, control, and shutdown — so you never have to fumble with the screen reader yourself. Instead of listening to spoken announcements, the output is returned as plain text that you can read, inspect, or save as a baseline for regression testing.

Inspecting A Live Page

Running the auto-vo CLI against a page dumps the entire VoiceOver reading sequence. For a well-structured site, the text output shows skip navigation links, semantic nav regions, and properly nested lists.

$ auto-vo --url https://smashingmagazine.com --limit 200 > output.txt
$ cat output.txt
link Jump to all topics
link Jump to list of all articles
link image Smashing Magazine
list 6 items
link Articles
link Guides 2 of 6
link Books 3 of 6
link Workshops 4 of 6
link Membership 5 of 6
More menu pop up collapsed button 6 of 6
end of list
end of navigation
...(truncated)

Heading structure is where issues start to show. The same tool can produce a list of just the headings VoiceOver encounters, which makes hierarchy problems obvious.

$ cat output.txt | grep heading
heading level 2 link A Complete Guide To Accessibility Tooling
heading level 2 link Spinning Up Multiple WordPress Sites Locally With DevKinsta
heading level 2 link Smashing Podcast Episode 39 With Addy Osmani: Image Optimization
heading level 2 2 items A SMASHING GUIDE TO Accessible Front-End Components
heading level 2 2 items A SMASHING GUIDE TO CSS Generators & Tools
heading level 2 2 items A SMASHING GUIDE TO Front-End Performance 2021
heading level 4 LATEST POSTS
heading level 1 link When CSS Isn’t Enough: JavaScript Requirements For Accessible Components
heading level 1 link Web Design Done Well: Making Use Of Audio
heading level 1 link Useful Front-End Boilerplates And Starter Kits
heading level 1 link Three Front-End Auditing Tools I Discovered Recently
heading level 1 link Meet :has, A Native CSS Parent Selector (And More)
heading level 1 link From AVIF to WebP: A New Smashing Book By Addy Osmani

That output reveals a page with a mix of level 1, level 2, and a stray level 4 heading. For screen reader users who navigate by heading outline, this breaks the expected flow. Having VoiceOver's actual announcement as text makes this kind of audit trivial to run and record.

Two Realistic Workflows

Acceptance Checking

When a feature is built against annotated design specs, the developer often wants a quick way to confirm the accessible name and role of each element. Writing the CLI output to a terminal or file produces a shareable artifact that a project manager or QA can compare against the design annotations.

$ auto-vo --url https://smashingmagazine.com --limit 100

Test-Driven Markup

The same core can be imported directly into a test runner. Inside a Node-based test suite such as Mocha, assertions can target the VoiceOver output for specific content. This shifts markup adjustments earlier, avoiding a refactor pass after the feature is declared done.

$ npm install --save-dev auto-vo
import { run } from 'auto-vo';
import { expect } from 'chai';

describe('loading example.com', async () => {
  it('returns announcements', async () => {
    const options = { url: 'https://www.example.com', limit: 10, until: 'Example' };

    const announcements = await run(options);

    expect(announcements).to.include.members(['Example Domain web content']);
  }).timeout(5000);
});

Driving VoiceOver Programmatically

Behind the scenes, Auto VO starts VoiceOver from the command line using a built-in CLI that ships with the application.

/System/Library/CoreServices/VoiceOver.app/Contents/MacOS/VoiceOverStarter

Navigation and announcement capture are handled by small JavaScript executables that send AppleScript instructions to VoiceOver. A notable example is extracting the most recent spoken phrase.

function run() {
  const voiceOver = Application('VoiceOver');
  return voiceOver.lastPhrase.content();
}

One operational constraint exists: VoiceOver requires AppleScript access to be enabled, which may mean custom configuration for CI runners. That requirement is the main caveat for using it in automated pipelines today.

A Note On Manual Testing Advice

The common advice to "turn on your screen reader for a few minutes and listen" is not a meaningful simulation of using VoiceOver daily. Screen readers are sophisticated, with steep learning curves; an unguided first session tends to cause confusion rather than insight. Auto VO removes that barrier by managing the tool completely and returning its output as readable text. The developer evaluates the content of the announcements, not their own ability to navigate the software blind.

Further Reading

Smashing Editorial