coder / coder/ghostty-web

Screen-reader accessibility: terminal output has no accessible representation (xterm.js screenReaderMode as reference)

Open
#187 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
2.8k
Forks
172
PR merge metrics
No merged PRs in 30d

Description

## Context

We are evaluating ghostty-web as a replacement for xterm.js in [Cloud Foundry Stratos](https://github.com/cloudfoundry/stratos) (SSH and Kubernetes terminals), attracted by the drop-in API aim and by the fact that the canvas renderer injects no `` elements — which makes it clean under a hardened CSP (`style-src-elem` with a nonce) where xterm.js is not. Our evaluation notes are recorded [here](https://github.com/cloudfoundry/stratos/issues/5705#issuecomment-5151706116) and [here](https://github.com/cloudfoundry/stratos/issues/5705#issuecomment-5151754380).

The API surface covered everything our component uses (`Terminal`, `FitAddon`, `open`, `write`, `onKey`, `onResize`, `dispose`). The one gap we found is accessibility, and it is the gap that blocks adoption for us, since screen-reader support is on our roadmap.

## What exists today

Reading the shipped `ghostty-web@0.4.0` dist: the hidden input element is decorated correctly — `role="textbox"`, `aria-label="Terminal input"`, `aria-multiline` — so focus and typing are visible to a screen reader.

## The missing piece

Terminal *output* has no accessible representation. It is canvas pixels with no parallel text alternative: no `aria-live` region, no equivalent of xterm's `screenReaderMode` in the options or published types. A screen-reader user can type into the terminal and hears nothing back.

## How xterm.js solves it, as a reference

xterm.js ships this behind an `ITerminalOptions.screenReaderMode` flag (default off, toggleable at runtime — an option-change listener instantiates or disposes the manager). When enabled, an `AccessibilityManager` maintains a parallel DOM structure alongside the rendered output:

- a live region with `aria-live="assertive"` that announces incoming output as it prints;
- navigable per-row elements so a user can move through the buffer manually;
- an overflow guard for floods ("Too much output to announce, navigate to rows manually to read").

The point that makes it relevant here: the mechanism is renderer-independent. The same machinery runs under xterm's WebGL renderer — that is VS Code's "screen reader optimized" terminal — so a canvas-rendered terminal and screen-reader access are not in tension. The announcement layer just has to exist alongside the canvas.

Since ghostty-web aims to be API-compatible with xterm.js, `screenReaderMode` seems like the natural shape for it here too: opt-in, no cost when disabled, and migrations with accessibility requirements can carry their existing option through unchanged.

Happy to share more detail from our evaluation if useful.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.