- Die Annahme, dass Terminal-Apps textbasiert und damit von Natur aus barrierefrei seien, zerbricht bei modernen TUIs; Frameworks wie Ink, Bubble Tea und tcell können für Screenreader-Nutzer sogar ein feindlicheres Umfeld schaffen
- CLIs arbeiten mit linearen
stdin/stdout-Streams, in denen sich Ausgaben chronologisch ansammeln, während TUIs das Terminal als zeichenzellenbasiertes 2D-Raster behandeln, dem Screenreader nur schwer folgen können - Bei
gemini-clizeichnet Ink den React-Komponentenbaum passend zum Terminalraster neu und bewegt dabei den Cursor zwischen Spinner, Timer und Gesprächsverlauf, was bei Speakup und NVDA wiederholtes Vorlesen, Abstürze und Eingabeverzögerungen auslösen kann - Ältere Werkzeuge wie
nano,vim,menuconfigund Irssi reduzieren den Lärm durch Koordinatenaktualisierungen und minimieren Störungen der Eingabezeile durch versteckten Cursor, Fokus auf einer einzelnen Spalte und die Nutzung von VT100-Scrollbereichen - Wer barrierefreie Terminal-Werkzeuge bauen will, sollte deklarative UI-Frameworks vermeiden, die das Terminal wie eine Leinwand behandeln und aggressiv neu zeichnen, und stattdessen ein Verhalten nahe an einfachen, linearen CLI-Streams sicherstellen
Das Missverständnis „Text ist also barrierefrei“
- Die Annahme, dass Anwendungen, die im Terminal laufen, von Natur aus barrierefrei sind, passt nicht zur realen Nutzungspraxis
- Die Erwartung, dass Screenreader rohen ASCII-Text leicht interpretieren können, weil es keine Grafik, kein komplexes DOM und keine WebGL-Canvas gibt, bricht bei modernen TUIs zusammen
- Terminal-UI-Frameworks wie Ink (JS/React), Bubble Tea (Go) und tcell sollen die Developer Experience (DX) verbessern, können für blinde Nutzer aber ein feindlicheres Umfeld schaffen
- In Sachen Barrierefreiheit sind moderne TUIs oft sogar schlechter als schlecht umgesetzte grafische Oberflächen
Strukturelle Unterschiede zwischen CLI und TUI
-
CLI: linearer Stream
- CLIs arbeiten auf Basis von
stdin/stdout; man gibt einen Befehl ein, das Ergebnis wird darunter angefügt und der Cursor wandert nach unten - Weil die Ausgabe linear ist und sich chronologisch aufbaut, passt sie gut zu kernelnahen Screenreadern wie Speakup
- CLIs arbeiten auf Basis von
-
TUI: 2D-Raster
- TUIs behandeln das Terminalfenster nicht als Textstream, sondern als 2D-Raster, in dem einzelne Zeichenfelder wie Pixel beschrieben werden
- Damit wird der zeitliche Fluss aufgegeben und ein räumliches Layout priorisiert, was für Screenreader schwer nachzuverfolgen ist
Die Probleme, die sich bei gemini-cli zeigen
gemini-cliist ein mit Node.js und dem Ink-Framework geschriebenes Werkzeug und wirkt oberflächlich wie ein simples Chat-Interface- Intern versucht Ink, den React-Komponentenbaum an das Terminalraster anzupassen
- Bei der Nutzung mit Speakup (Linux) oder NVDA (Windows) scheitert die Anwendung nicht einfach nur, sondern überschüttet den Screenreader fortlaufend mit neu zu lesenden Inhalten
-
Ein Bildschirm, der sich wie eine reaktive Leinwand verhält
- Das Framework behandelt den Bildschirm wie eine reaktive Leinwand, weshalb jede Aktualisierung ein Neuzeichnen auslöst
- Während die KI „nachdenkt“, bewegt es den Hardware-Cursor zur Position des Timers, schreibt die neue Zeit und setzt ihn dann wieder zurück
- Für sehende Nutzer ist das ein sofort vorübergehender Vorgang, für Screenreader-Nutzer klingt es aber wie „Responding... Time elapsed 1s... Responding... Time elapsed 2s...“ in Dauerschleife
- Während der Cursor kurz zwischen Statusanzeige, Spinner und Gesprächsverlauf springt, versucht Speakup jeweils den Inhalt unter dem Cursor vorzulesen
- Dadurch vermischen sich Timer-Aktualisierungen und Gesprächsfragmente, sodass es schwer wird, sich auf die eigentliche Eingabe zu konzentrieren
-
Instabilität mit NVDA und beim Einfügen
- Wenn man unter Windows mit NVDA ein Terminal öffnet, sich per SSH auf ein Linux-System verbindet, an eine
screen-Sitzung anhängt und dann Text einfügt, kann NVDA sofort abstürzen oder das System stark instabil werden - Jedes eingegebene Zeichen und jedes eingefügte Textstück verändert den Zustand der Anwendung, woraufhin das Framework entscheidet, dass die Oberfläche neu gerendert werden muss
- Wenn der Gesprächsverlauf Teil dieses Zustands ist, versucht es sofort, das Layout von tausenden Textzeilen neu zu berechnen oder neu zu zeichnen
- Je mehr Gesprächsnachrichten vorhanden sind, desto häufiger tritt dieses Problem auf
- Selbst die Tastenkombination
Insert+5, mit der dynamische Inhaltsmeldungen vermieden werden sollen, hilft hier nicht
- Wenn man unter Windows mit NVDA ein Terminal öffnet, sich per SSH auf ein Linux-System verbindet, an eine
-
Schleife aus Eingabeverzögerungen
- Wenn Frameworks wie Ink in einer Single-Thread-Umgebung wie Node.js laufen, nimmt der Leistungsabfall mit wachsendem Verlauf stark zu
- Beim Einfügen großer Textblöcke müssen Differenzen über tausende Zeilen berechnet werden
- Das System ist dann damit beschäftigt zu ermitteln, wie der Bildschirm neu gezeichnet werden soll, und verarbeitet Eingaben verspätet
- Schon nach einem einzelnen Tastendruck kann man bis zu 10 Sekunden warten, bis das Zeichen wieder angezeigt wird
Warum alte Werkzeuge funktionieren
- Werkzeuge wie
nano,vimundmenuconfigwerden nicht genutzt, weil sie in Sachen Barrierefreiheit immer perfekt wären - Entscheidend ist vielmehr, dass diese Werkzeuge den Cursor vollständig verbergen oder den Lärm aus der Cursorpositionsverfolgung reduzieren können
-
nanoundvim: Cursor verbergen- Wenn man
nanomit Optionen wie--constantshowstartet, die die Cursorposition anzeigen, odervimohne bestimmte Einstellungen nutzt, kann die Benutzbarkeit zusammenbrechen - Wenn der Cursor sichtbar ist und die Verfolgung aktiv bleibt, priorisiert Speakup Cursorpositionsaktualisierungen gegenüber dem Zeichen-Echo
- Gibt der Nutzer „a“ ein, hört er statt „a“ dann „Column 2“, bei „b“ entsprechend „Column 3“
- Diese alten Werkzeuge lassen sich so konfigurieren, dass visuelle Cursor- oder Statusleistenaktualisierungen unterdrückt werden, sodass sich der Screenreader auf den Zeichen-Eingabestream statt auf Koordinatenaktualisierungen stützt
- Moderne Frameworks bieten in der Regel keinen „no-cursor“- oder „headless“-Modus und setzen voraus, dass ein sichtbarer Cursor unverzichtbar ist
- Wenn man
-
menuconfig: Fokus auf einer einzelnen Spalte- Das
menuconfigdes Linux-Kernels funktioniert, weil es strikt einen Fokus in nur einer Spalte beibehält - Es gibt zwar Rahmen und Überschriften, aber der aktive Bereich ist eine vertikale Liste, und der Cursor bleibt auf diese Liste fixiert
- Er springt nicht zur Aktualisierung einer Uhr nach rechts unten und danach zur Titelaktualisierung nach links oben
- Die räumliche Komplexität bleibt gering, sodass Screenreader nicht die Orientierung verlieren
- Das
-
Irssi: Nutzung von Scrollbereichen
- Irssi ist nicht zufällig zugänglich, sondern ein Chat-Werkzeug, das seit mehr als 20 Jahren über eine eigene Rendering-Engine VT100-Scrollbereiche nutzt
- Wenn eine neue Nachricht ankommt, weist es den Terminaltreiber an: „Definiere Zeile 1 bis 23 als Scrollbereich“
- Danach sendet es den Befehl zum Hochscrollen, das Terminal verschiebt den Inhalt nach oben und zeichnet den neuen Text unten in diesem Bereich ein
- Auf diese Weise werden Störungen der Eingabezeile minimiert
- Statt jedes Zeichen auf dem Bildschirm manuell neu zu schreiben, stützt es sich auf Hardware-Funktionen des Terminals
- Moderne Frameworks ignorieren solche Hardware-Funktionen oft, berechnen stattdessen Zustandsdifferenzen des Bildschirms und schreiben Zeichen neu; das ist rechenintensiver und barrierefeindlich
Die Probleme bei der Bearbeitung von gemini-cli-Issues
- Google und die Maintainer von
gemini-cliwirken zwar so, als nähmen sie Barrierefreiheit ernst, im Repository bleiben aber wichtige Regressionen in der Barrierefreiheit liegen - Bei Barrierefreiheitsregressionen wie Issue #3435 und Issue #11305 gibt es weder Diskussion, noch Roadmap, noch Fixes
- Issue #1553 sollte genau solche Ausfälle der Barrierefreiheit nachverfolgen, wurde aber nicht gelöst und stattdessen automatisch von einem Bot geschlossen
- Der Bot schließt das Issue mit einer Standardformulierung, wonach es wegen langer Inaktivität und zur Pflege des Backlogs beendet werde
- Einen Bericht zur Barrierefreiheit zu schließen, nur weil Maintainer monatelang nichts daran getan haben, ist keine Aufräumarbeit, sondern eher das Verbergen von Belegen
- Es sendet das Signal, dass Bugs verschwinden, wenn man sie nur lange genug ignoriert, während die Software für blinde Nutzer in der Praxis weiterhin unbenutzbar bleibt
- Die Kennzahl „Closed Issues“ des Projekts mag dadurch besser aussehen, die Barrierefreiheitsprobleme sind damit aber nicht gelöst
Fazit für den Bau barrierefreier Terminal-Werkzeuge
- Wer Barrierefreiheit bei Terminal-Anwendungen ernst nimmt, sollte aufhören, deklarative UI-Frameworks zu verwenden, die das Terminal wie eine Leinwand behandeln
- „Moderne“ TUI-Stacks sind darauf optimiert, dass Entwickler Code bequem wie in React schreiben können, und opfern dafür die Fähigkeit der Maschine, Text effizient zu rendern
- Wenn eine Anwendung nicht sicherstellt, dass Nutzer den Cursor verbergen können, oder auf aggressives Neuzeichnen angewiesen ist, um Spinner und Timer anzuzeigen, wird sie zu einem unzugänglichen Werkzeug
- Für blinde Nutzer ist ein einfacher, linearer CLI-Stream weitaus besser als eine „smarte“ TUI, die verzögert reagiert, ständig neue Inhalte vorliest und den Cursor über den ganzen Bildschirm verstreut
1 Kommentare
Lobste.rs-Kommentare
Dieser Artikel riecht wie viele andere Blogposts stark nach KI-unterstütztem Schreiben
LLMs lieben solche Titel: „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“
Das hätte ein großartiger Blogpost sein können, aber wenn es wirkt, als hätte der Autor nur eine Gliederung in ChatGPT geworfen und es dann dabei belassen, ist das für Leser und Autor gleichermaßen ein Verlust
Genauso, wenn ein sehr spezielles einmaliges Problem als „klassisches“ Problem bezeichnet wird
Wirklich deprimierend. Kurz gesagt: Es gibt zugängliche TUIs wie Irssi, aber moderne TUI-Frameworks ignorieren solche Vorbilder und verlassen sich stattdessen auf Grid-Diffs und Cursorbewegungen
Screenreader lesen den Inhalt an der Cursorposition vor, wenn sich der Cursor bewegt, wodurch das Ergebnis durcheinandergerät oder es zu massivem Vorlese-Spam kommt
Ich bin nicht sicher, ob die technische Erklärung hier vollständig korrekt ist
Insbesondere unterstützte Ink lange Zeit überhaupt kein inkrementelles Rendering, und die meisten mit Ink gebauten Apps aktivieren es noch immer nicht. Dieses inkrementelle Rendering ist zudem zeilenbasiert und bewegt den Cursor nicht tatsächlich zur Position eines Timers
Gemini CLI benötigt zum Aktivieren des inkrementellen Renderings die Nutzung eines Alternate Buffers, was deaktiviert wird, wenn der eingebaute screenreaderfreundliche Modus aktiv ist, wie hier zu sehen ist. Die Dokumentation zur entsprechenden Option ist hier
Außerdem sind Python rich/textual trotz einer langsameren und meist Single-Thread-orientierten Sprache oft deutlich schneller als Ink. Das Berechnen von Diffs über Tausende Zeilen hinweg ist nicht zwangsläufig so langsam und dauert auch keine 10 Sekunden
Ich zweifle nicht daran, dass die User Experience frustrierend und kaputt ist, aber die konkret angegebene Ursache wirkt womöglich wie eine LLM-Halluzination oder basiert auf unvollständigen Informationen. Das inkrementelle Rendering von Ink funktioniert selbst dann nicht so, wie es beschrieben wird
In Wirklichkeit ist es wahrscheinlich eher so, dass vollständige Screen-Redraws Screenreader verwirren und zeilenbasierte Redraws dazu führen, dass beliebige zusammenhanglose Textfragmente erneut vorgelesen werden, auch wenn sie mit der Änderung nichts zu tun haben
Es ist nicht fair, nur den TUIs die Schuld zu geben
Das eigentliche Problem ist, dass die Barrierefreiheitsunterstützung fast im gesamten Stack miserabel ist
Erstens verwenden die meisten GPU-gerenderten Terminalemulatoren überhaupt keine vom System bereitgestellten Accessibility-APIs. Wenn Text per GPU gerendert wird, können Accessibility-Tools ihn nicht „lesen“ und sehen nur ein Bild. Dazu gehören Kitty, Alacritty und WezTerm. Mein Terminal Ghostty ist unter macOS über Accessibility-APIs lesbar, ebenso iTerm2 und Terminal.app
Zweitens gibt es keinerlei Terminal-Sequenzen oder standardisierte Verfahren, mit denen TUIs Barrierefreiheitsinformationen an den Terminalemulator übergeben könnten. Es bräuchte etwas wie ARIA-artige Annotationen für Terminalzellen, Laufbereiche und Regionen, aber solche Versuche gibt es nicht. Selbst wenn ein TUI den Cursor sauber handhabt, wird es in vielen Anwendungsfällen Probleme geben
Als Beispiel habe ich in Ghostty an der Integration von OSC133 mit Accessibility-APIs gearbeitet, um jeden Shell-Prompt, jede Eingabe und jeden Befehl nicht nur als einfache Textbox, sondern als strukturell sinnvolle Elemente sichtbar zu machen. Das zeigt, dass Terminal-Spezifikation, TUI und Terminalemulator zusammenpassen müssen
Der gesamte Stack ist verrottet, und es gibt kaum Leute, die ihn ernsthaft reparieren wollen. Auch ich kann mit meiner begrenzten Zeit nur mein Bestes tun, aber das ist ein riesiges Thema, das sogar Ökosystem-Politik erfordert, und kaum zu bewältigen
Als Bonus ist die zugleich coole und schreckliche Realität, dass KI hier tatsächlich bei Verbesserungen der Barrierefreiheit hilft. Viele KI-Tools nutzen oder missbrauchen Accessibility-APIs, um Fensterlisten zu lesen und Eingaben auszuführen. Deshalb beginnen mehr Apps, Accessibility-Integration wegen KI-Anwendungsfällen deutlich ernster zu nehmen
Dass Claude Code und gemini-cli nicht auf readline basieren, macht mich jeden Tag wütend
Sie haben ein paar ähnliche Tastenbelegungen eingebaut, aber der lange Schwanz vertrauter readline-Shortcuts fehlt
Anthropic könnte ruhig zugeben, dass die Entscheidung „wir müssen es wie Webentwicklung machen“ ein Fehler war, und wieder bei readline anfangen
Die Vorstellung, dass die vertraute Entwicklererfahrung der Leute, die solche Tools bauen, wichtiger sei als die vertraute Benutzererfahrung der Leute, die sie benutzen, ist falsch
Es gibt tatsächlich kaum bekannte Drittanbieter-Lösungen, die gut gepflegt werden. Wenn man eine flexible Eingabebox braucht, muss man sie von Grund auf selbst bauen
Im Gegensatz zum hervorragenden Input-Widget von Textual oder einer anderen Bibliothek im JS-Ökosystem, OpenTUI
Ich mag LLMs nicht, deshalb ist eine schlechte UI für mich persönlich eher ein Vorteil, aber es könnte Gründe geben, readline nicht zu verwenden
Ich glaube, Terminal-Editoren wie kakoune und helix werden es schwer haben, Barrierefreiheitsanforderungen zu erfüllen, sofern sie nicht den Trick des „Cursor-Versteckens“ nutzen
Selbst dann sind sie wahrscheinlich nicht so zugänglich wie VS Code
Welche zugänglichen plattformübergreifenden IDE-lites oder IDEs außer VS Code gibt es eigentlich? Die zunehmend feindselige Haltung von VS Code gefällt mir nicht. Vielleicht die IDEs von JetBrains
Der Nachteil ist, dass Emacs selbst zwar plattformübergreifend ist, emacspeak wegen TTS aber möglicherweise schwach von Linux abhängt. Vielleicht auch nicht. Unter Windows habe ich es nie ausprobiert
Für blinde Menschen braucht man emacspeak oder die Accessibility-Tools der jeweiligen Plattform für Sehbehinderte
Barrierefreiheit ist ein Spektrum, keine Checkbox
Links hat einen eigenen Braille-Terminalmodus, der gefälschte GUI-Elemente in einfachere Vollbildmenüs umwandelt und die Navigation per Pfeiltasten ebenfalls auf Zeilenbasis umstellt
Ein weiteres interessantes Beispiel ist edbrowse. Das ist ein Textmodus-Browser des blinden Entwicklers Karl Dahlke, der im Gegensatz zu populäreren Textmodus-Webbrowsern keine TUI verwendet, sondern eine Befehlszeilenschnittstelle im Stil von ed
Wenn es sich um das Ink-Framework handelt, ist das wahrscheinlich der Grund, warum die CLI 100 % CPU verbraucht, für immer hängen bleibt und lange Chatverläufe immer wieder neu rendert. Schade