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

Status Open
Maintainer reply None cached
Activity 6 comments · opened Jun 23, 2026

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 ↗

4 Comments

github-actions[bot] · 2 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/58429
  2. https://github.com/anthropics/claude-code/issues/69996
  3. https://github.com/anthropics/claude-code/issues/48654

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

DCA11Y · 2 months ago

Thanks, bot. This isn't a duplicate of any single one of these, but it does overlap them, so let me connect the dots rather than close it.

This issue is meant as an umbrella: bake the whole non-visual accessibility layer into Claude Code rather than track each piece in isolation. The flagged issues each cover one slice:

  • #58429 (mine) speaks responses aloud, which overlaps the TTS announcements piece here.
  • #69996 (@JoshMiele) covers heading structure, landmarks, and focus, overlapping the heading-discipline piece.
  • #48654 covers audio on permission prompts in VSCode, overlapping the earcons and accessible-prompts pieces.

I'd like to keep this open as the consolidated request, but happy to split it into child issues if that shape is easier for the team to triage.

ampage87 · 29 days ago

Adding a data point from the other side of the platform matrix — I'm blind, Windows 11 with JAWS 2026 (and NVDA), where you're on macOS with VoiceOver. Different stack, overlapping problems.

I've just filed #83167 about a specific defect in the Claude Desktop GUI (as opposed to the CLI/TUI most of this thread covers): the chat message list renders a fixed maximum of about ten messages and removes the rest from the DOM entirely. Measured directly — forcing the message container to 9000px tall still rendered exactly ten, and 200px also rendered exactly ten, so it's a hard item-count cap rather than anything viewport-driven.

This connects directly to your point 4 on heading discipline. Reliable heading navigation is exactly the right ask, and it's what I reach for too — but it can't work if the content isn't in the accessibility tree in the first place. On a 60-message conversation I can reach about ten of them by any means, and the set shifts underneath me as I move, so the buffer is never stable. Heading structure and content persistence are the same problem viewed from two ends.

Worth noting for scope: the desktop app loads its chat UI from claude.ai, so this one affects the web product identically. One fix covers both.

Also seconding your central argument. I got to a working setup only by enabling the app's own developer-tools switch and running a console script that refuses to unmount message elements — which is precisely the "first, become an expert in the tool's extension system" floor you're describing. It works well, and no screen reader user should have to do it.

w9fyi · 16 days ago

Adding a second data point: I'm also a blind developer (VoiceOver, macOS) using Claude Code daily, and this is a real gap for me too — specifically I have no accessible way to tell when the VS Code extension is actively working vs. idle/done, so I can't reliably time when to shift focus to read the transcript. +1 on native TTS/earcons for lifecycle events; would use this immediately if it shipped.

Showing cached comments. Read the full discussion on GitHub ↗