Solution overview
The most practical way to give screen‑readers distinct, semantic information about each prompt segment while preserving existing colors and Unicode symbols is to embed non‑printing OSC 133 (Operating System Command) prompt‑mark sequences around the visible text. These sequences are ignored by terminals that do not understand them, but terminals that support the prompt markup extension (iTerm2, GNOME Terminal/VTE, Windows Terminal ≥ 1.19) expose the marked ranges as accessible objects that screen readers can announce.
How it works
- Each logical segment (e.g., current directory, git branch, return code) is wrapped in a pair of
OSC 133;A;…\007 start and OSC 133;B;…\007 end markers.
- The
A argument conveys the segment type (a short token such as dir, git, ret).
- The
B argument carries the visible text for that segment.
- All normal ANSI color escapes (
%F{…}%f, %K{…}%k) remain inside the visible text, so colors are unchanged.
- The
%{…%} construct continues to hide non‑printing characters from Zsh’s prompt‑length calculation.
Implementation steps
- Add a helper function to your
~/.zshrc (or a custom Oh My Zsh plugin):
# Print an OSC 133 prompt‑mark segment
# $1 = segment type (e.g., dir, git, ret)
# $2 = visible text for the segment
prompt_mark() {
printf '\e]133;A;%s\007' "$1"
printf '%s' "$2"
printf '\e]133;B;%s\007' "$1"
}
- Define a function that builds the full prompt using the helper. Example for a simple three‑segment prompt:
build_prompt() {
local dir git ret
dir="%~" # current directory
git="$(git_prompt_info)" # assumes the git plugin is loaded
ret="%?" # last return code
PROMPT="$(prompt_mark dir "$dir") "
PROMPT+="$(prompt_mark git "$git") "
PROMPT+="$(prompt_mark ret "$ret") %# "
}
- Hook the builder into
precmd so the prompt is refreshed before each new prompt, and into preexec to clear any leftover markers (optional but tidy):
precmd_functions+=(build_prompt)
preexec_functions+=(() { # reset PROMPT to avoid stale markers
unset PROMPT
})
- Reload the shell (
source ~/.zshrc) and verify:
- Run
echo $PROMPT – you should see only the visible segments; the OSC sequences are invisible.
- In a terminal that supports OSC 133 (e.g., iTerm2), enable the built‑in “Prompt Mark” accessibility feature or use a screen reader (NVDA, VoiceOver) and confirm each segment is announced separately.
- In a terminal without OSC 133 support, the prompt should appear exactly as before, with colors intact.
Established terminal accessibility protocols
- OSC 133 Prompt Mark – defined by iTerm2 and adopted by VTE‑based terminals (GNOME Terminal, Tilix, etc.) and recent Windows Terminal releases. It allows the shell to tag ranges of the prompt with arbitrary strings.
- OSC 8 Hyperlink – while useful for clickable URLs, it does not convey semantic segment types and is therefore less suitable for this use case.
- ARIA‑like attributes via
OSC 7 – not widely implemented for prompt segmentation.
If your target terminal does not implement OSC 133, the fallback is to rely on plain text segments (ensuring all color escapes are hidden with %{…%}) – this still yields a readable prompt for screen readers, albeit without explicit semantic labels.
Missing diagnostic detail
To confirm whether the OSC 133 approach will work for you, please let me know which terminal emulator you primarily use (e.g., iTerm2, GNOME Terminal, Windows Terminal, Konsole, etc.). This determines whether the prompt‑mark extension is available and whether any additional configuration is needed.