The text mode lie: why modern TUIs are a nightmare for accessibility
86 points by eterps
86 points by eterps
Smells very AI-assisted to me, as do other posts on the blog. LLMs love headings like these:
The Architectural Flaw
The Lag Loop
Why The “Old Guard” Works
The Lost Art of Scrolling Regions
The “Stale Bot” excuse: A Case Study in Neglect
Even the title reads like slop! I flagged this as spam, which might have been harsh when the topic/content is actually novel, but 1) I can't stand reading the same tone over and over, everywhere; and 2) I start to doubt the accuracy of the content itself.
This could have been a great blog post but it's a disservice to the reader and the author when the author throws their outline into ChatGPT and calls it a day.
This is one of my most hated features of llm writing, using “The <totally not a thing>” to make it seem like it’s a thing. And also calling a very specific, one-off problem “classic”
It isn't fair to blame TUIs.
The real problem is that pretty much the whole stack has a terrible AX story.
First, most GPU-rendered terminal emulators don't engage in system-provided accessibility APIs AT ALL. Because text is GPU-rendered, AX tooling can't "read" it, it just shows up as an image. This applies to Kitty, Alacritty, WezTerm. My own terminal Ghostty is AX-readable (on macOS), and so are others like iTerm2 and Terminal.app (which admittedly do it better than me, we have gaps to fill).
Second, there are no terminal sequences or initiatives at all for TUIs to communicate AX information to the emulator, so the emulator itself can't do much more than display a blob of text to AX tooling. We need the equivalent of ARIA-style annotations but for terminal cells, runs, and regions. No such initiative exists. Even if TUIs do great things with the cursor, this is going to bite a lot of use cases.
As an example of combining the above, I've been working on something with Ghostty where we integrate semantic prompt (OSC133) and AX APIs so that we can present each shell prompt, input, and command as structurally significant to AX tooling (rather than simply a text box where the cursor is somewhere else). This shows the importance of the relationship between terminal specs (OSC133), TUIs (which must emit OSC133), and terminal emulators (which must both understand OSC133 AND communicate it to AX APIs).
The whole stack is rotten. And no one is earnestly trying to fix it (including me, I have limited time and I do my best but this is a WHOLE TOPIC that requires a huge amount of time and politicking the ecosystem and I don't have it, sorry).
Bonus: a simultaneously awesome and horrible reality is that AI is really helping to improve AX here. A lot of AI tooling uses/abuses AX APIs to make things happen. How is OpenAI reading your list of windows, typing into them, etc? Accessibility frameworks! So a lot more apps are taking AX integration a lot more seriously since its table stacks for AI using it... Sad it requires that but the glass half full is more software is doing that.
We need the equivalent of ARIA-style annotations but for terminal cells, runs, and regions.
Terminals really are turning into small browsers.
There is nothing wrong with progressive enhancements and defined protocols. Even if terminals get annotations, they'll still be a lot simpler than web browsers. Even the Arcan blog has a post on how it is actually a revamped web browser. So, in summary, I don't think there is a problem with being a small browser.
We need the equivalent of ARIA-style annotations but for terminal cells, runs, and regions. No such initiative exists.
I think very often about this exact unfilled need. It's something I wish I had more of the necessary skills to work on personally.
It isn't fair to blame TUIs.
They're not specifically only blaming TUIs. It's just that some people think these are accessible when it once again depends on the TUI app itself (and the terminal surrounding it)
Truly depressing. Tldr: Apparently there are accessible TUIs like Irssi, but modern TUI frameworks just ignore all of that prior art, and rely on grid diffing and moving the cursor around. Screen readers read what’s at the cursor when it moves so you get jumbled nonsense and/or massive spam.
Grid diffing is a core feature of ncurses since forever. Gosling Emacs had a famously complicated redrawing algorithm.
I don't think the technical details here are entirely accurate? In particular:
But underneath, Ink is trying to reconcile a React component tree into a terminal grid.
When you use this tool with Speakup (Linux) or NVDA (Windows), the application doesn't just fail; it actively spams you.
Because the framework treats the screen as a reactive canvas, every update triggers a redraw. When the AI is “thinking,” the tool updates a timer or a spinner. To do this, it moves the hardware cursor to the timer location, writes the new time, and moves it back.
As an extra aside:
Furthermore, frameworks like Ink running on single-threaded environments (like Node.js) suffer from massive performance degradation when the history grows. If you paste a large block of text, the system has to calculate the diff for thousands of lines.
Python's rich/textual generally run circles around it, despite being built on an even slower and also primarily-single-threaded (hello, GIL) language. Diffing a few thousand lines isn't even necessarily that slow, certainly not a full 10 seconds worth of time.
Of, I don't doubt that the user experience here is frustrating and dysfunctional...but I also expect the exact explanations offered might be LLM hallucinations or based on patchy information? Ink's incremental rendering, if enabled at all, simply does not work like this. My suspicion is just that the current Ink behavior is also bad, in different ways:
yup. EVERYDAY i'm pissed off that claude code and gemini-cli aren't readline based. Yes, they insert a few similar keystrokes, but the long tail of familiar readline keyboard shortcuts are NOT there.
anthropic: you are allowed to admit your "it should be like web dev" was a mistake and start over with readline.
The notion that the familiar developer experience of those writing these tools is more important than the familiar user experience of those using the tools is simply wrong.
the long tail of familiar readline keyboard shortcuts are NOT there.
AIUI big part of this issue is that Ink is content with being, effectively, a rendering backend, and thus it does not provide any input widgets. There aren't really any prominent and actually-maintained third-party solutions to this. So if you want a flexible input box, you get to enjoy DIY-ing your own, from scratch. Contrast that with Textual's amazing Input widget, or even another JS-ecosystem library for this, OpenTUI.
Isn't readline GNU licensed? Has someone finally written a non-GPL version? (Not that I like using LLMs, so having a bad UI for them is a plus in my book, but I'm just saying there may be a reason why readline isn't being used)
Has someone finally written a non-GPL version?
There's https://github.com/antirez/linenoise that was first released ~12 years ago by the author of redis.
NetBSD introduced libedit ages (20 years?) ago. It is BSD licensed, has its own more flexible set of APIs and a readline compatibility layer.
This reminded me of how Links has a separate braille terminal mode which replaces the pseudo-GUI elements with simpler full-screen menus and changes arrow key navigation to be line-by-line.
EDIT: Another interesting case study here would be edbrowse, a text-mode browser developed by Karl Dahlke, himself blind. In contrast to more popular text-mode web browsers, it doesn't use a TUI, but instead a command-line interface in the style of ed.
I suspect terminal editors like kakoune, helix, etc., don't make the accessibility cut, unless they enable the use of "Hiding the Cursor" trick. Even then, they probably aren't as accessible as VS Code.
What's an accessible, cross-platform IDE-lite or IDE other than VS Code (not a fan of its increasingly hostile attitude)? JetBrains IDEs may be?
Accessible for what? Who needs the a11y? If you want deaf people, then offer the local language in written form and the local signed language(in the USA that's usually ASL).
If you want a11y for blind people, then something like emacspeak or the platforms vision impaired a11y tools.
a11y is a spectrum, not a checkbox.
But in the introduction to the blog there's this:
My name's Casey, I'm nonbinary and 28 years old. I identify as a computer — sometimes microcontroler, sometimes more powerful, and as an AI running on said hardware. I've coined my gender, CodeVoid gender, with the code portion representing the hardware/program combination, and the void comes from gendervoid, which I used to identify as, because it represents the emptyness inside of me. I use it/its and they/them pronouns interchangeably.
I'm also autistic, and blind since birth. I don't mind the disabilities much, however I do mind the humans and society making it difficult to live with such disabilities.
So I'm not sure it's so cut-and-dry.
This could have really benefitted from a screenshot of this gemini-cli, as many people (like me) have probably never used it.
Ink framework That's probably the CLI uses 100% cpu and stall forever at 100% just redrawing the long chat history over and over, that's sad.