Make Claude Code usable out-of-the-box for blind/screen-reader users (audio cues, heading discipline, humanized announcements)

Open 💬 2 comments Opened Jun 23, 2026 by DCA11Y

Who I am

I'm a blind Lead Accessibility Architect. I drive Claude Code entirely by keyboard and screen reader (VoiceOver on macOS, plus JAWS/NVDA/TalkBack in my testing work). Claude Code is my primary interface to my work, every day.

The problem

Claude Code is usable for me today, but only because I rebuilt an entire accessibility layer on top of it myself using hooks, shell scripts, and a third-party TTS tool. None of this ships in the product. A blind user who installs Claude Code fresh gets none of it. I'd like to see these capabilities baked into the official CLI and Desktop experience so the next blind developer doesn't have to reverse-engineer the hook system to get a working setup.

Here is everything I had to build, framed as features the team should consider natively.

1. Spoken announcements for agent activity (TTS)

A long agent turn is silent dead air for a screen reader user. I can't see the spinner, the "running Bash…" status, tool calls, permission prompts, or completion. I wired every hook event (PreToolUse, PostToolUse, PostToolUseFailure, Notification, PermissionRequest, Stop, SubagentStart, SubagentStop, TaskCompleted) to a TTS layer so I hear what the agent is doing in real time.

Ask: A built-in, opt-in spoken-status mode. Speak tool starts, completions, failures, permission requests, and turn completion. Make voice, rate, and verbosity configurable.

2. Humanized tool/command announcements

Raw tool names and shell commands are miserable to hear read aloud. mcp__<uuid>__getJiraIssue or a 200-character piped cd … && bash script.sh | jq … is noise. I wrote normalizers that turn getJiraIssue into "Get jira issue," flag MCP tools with a spoken "MCP" prefix so I know it's an external server, and reduce a Bash pipeline to just its meaningful first stage (e.g. "git commit", "bash validate-vault.sh") instead of reading every flag and path.

Ask: When speaking activity, announce a humanized summary, not the raw identifier. Camel/snake case split into words, MCP server calls clearly marked as external, commands summarized rather than read verbatim.

3. Audio earcons for state changes

Distinct short sounds for "needs your permission," "turn finished," "tool failed," "subagent started/finished." A non-visual user should be able to hear a permission prompt is blocking them without reading the screen. (My TTS tool had an earcon player, but its detached audio process died before the sound played, so I had to write a subshell wrapper to make chimes reliable. The point: earcons are valuable enough that I worked around a bug to keep them.)

Ask: Native, configurable earcons mapped to key lifecycle events, with a way to enable/disable and set volume.

4. Predictable heading structure in responses

I navigate responses with VoiceOver's "jump to next heading." If a response doesn't start with a top-level heading, that command lands somewhere arbitrary or skips the start entirely. I enforce "every response begins with an H1 and headings never skip levels" via a Stop hook that audibly warns me when the model drifts.

Ask: An optional output-formatting mode that guarantees a leading top-level heading and strict heading nesting (no skipped levels, no runaway deep headings), so screen reader heading navigation is reliable. This is a model-output contract, which is exactly the kind of thing that's hard for a user to enforce from outside and belongs in the product.

5. Accessible permission prompts and questions

Permission requests and multiple-choice questions are the highest-stakes interactions, since they block progress. They need to be announced immediately, clearly, and distinctly from ordinary output, and the available choices need to be readable in a linear, screen reader friendly way.

Ask: Treat permission prompts and interactive questions as first-class accessible surfaces: announced on appearance, options enumerated cleanly, current selection always spoken.

Why bake it in

Everything above lives in my personal ~/.claude/scripts/ and settings.json as hooks plus a third-party npm TTS package. It's fragile, it breaks on updates, and crucially it required me to already understand Claude Code's hook architecture before I could even start. The accessibility floor for a developer tool shouldn't be "first, become an expert in the tool's extension system." I'm very happy to share my scripts, configs, and the specific normalization rules with the team as a reference implementation if that's useful.

Environment

macOS, VoiceOver. Claude Code CLI and Desktop. Happy to provide my hook configuration and scripts on request.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗