Oh My Zsh Prompt System ↔ Screen Reader Accessibility: Exposing Semantic Segment Information
0 reputation · 02 Jan 2024, 15:24 UTC
0 reputation · 02 Jan 2024, 15:24 UTC
Oh My Zsh’s prompt and theme system currently renders the entire prompt line as plain text, so assistive technologies cannot distinguish individual segments such as the current directory, Git branch, or return code. The goal is to enable screen‑reader users to perceive these segments as distinct, semantically labeled elements without breaking existing color schemes or Unicode powerline symbols.
Achieving this faces several constraints: behavior varies across terminal emulators (e.g., iTerm2, GNOME Terminal, Windows Terminal), there is no standardized way to embed accessible annotations in Zsh prompts, and any solution must remain compatible with older Oh My Zsh releases that lack newer hook mechanisms. Additionally, the approach should not interfere with user‑customized themes or cause unexpected line‑wrap shifts that affect keyboard navigation.
What mechanism could be added to Oh My Zsh to let themes emit accessible annotations alongside existing ANSI escapes? How can the precmd and preexec hooks be used to provide semantic segment metadata that screen readers can interpret? Are there established terminal accessibility protocols or extensions that could be adapted for this purpose?
32775 reputation · 03 Jan 2024, 02:07 UTC
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.
OSC 133;A;…\007 start and OSC 133;B;…\007 end markers.A argument conveys the segment type (a short token such as dir, git, ret).B argument carries the visible text for that segment.%F{…}%f, %K{…}%k) remain inside the visible text, so colors are unchanged.%{…%} construct continues to hide non‑printing characters from Zsh’s prompt‑length calculation.~/.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"
}
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") %# "
}
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
})
source ~/.zshrc) and verify:echo $PROMPT – you should see only the visible segments; the OSC sequences are invisible.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.
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.
Use comments to ask for clarification. Post a solution as an answer.
32,775 reputation · 02 Jan 2024, 22:34 UTC
While using OSC sequences for semantic marking is a viable path, a critical implementation detail for Zsh is the precise use of the %{...%} wrapper. Zsh calculates the cursor position based on the number of characters sent to the terminal; if the OSC 133 sequences are not wrapped in these percent-brackets, the shell will miscalculate the prompt length.
This leads to visual corruption where the cursor drifts or the line wraps prematurely during long command entries. To ensure compatibility across Oh My Zsh themes, any helper function must encapsulate the escape sequences as follows:
# Correct wrapping for Zsh prompt length calculation
prompt_mark() {
printf '%%{%%e]133;A;%s%%007%%}' "$1"
printf '%s' "$2"
printf '%%{%%e]133;B;%s%%007%%}' "$1"
}
Verification is required for terminals that do not support OSC 133 to ensure these sequences are completely ignored rather than printed as literal text, which would otherwise break the visual layout for non-assistive users.