- A suposição de que apps de terminal são inerentemente acessíveis por serem baseados em texto não se sustenta nos TUIs modernos, e frameworks como Ink, Bubble Tea e tcell podem criar um ambiente ainda mais hostil para usuários de leitores de tela
- O CLI acumula saída em ordem cronológica como um fluxo linear de
stdin/stdout, mas o TUI trata o terminal como uma grade 2D de células de caracteres, o que dificulta para leitores de tela acompanhar o fluxo - O
gemini-clifaz o Ink redesenhar a árvore de componentes React para ajustá-la à grade do terminal, movendo o cursor entre spinner, timer e histórico da conversa, o que pode provocar leitura repetida, travamentos e atraso de entrada no Speakup e no NVDA - Ferramentas antigas como
nano,vim,menuconfige Irssi reduzem o ruído de atualizações de coordenadas e minimizam a interferência com a linha de entrada usando ocultação do cursor, foco em coluna única e áreas de rolagem do VT100 - Para criar ferramentas de terminal acessíveis, é preciso evitar frameworks de UI declarativa que tratam o terminal como um canvas e evitar redesenhos agressivos, garantindo um comportamento mais próximo de fluxos de CLI simples e lineares
O equívoco de que “por ser texto, é acessível”
- A suposição de que aplicações executadas no terminal são inerentemente acessíveis não corresponde ao uso real
- A expectativa de que leitores de tela consigam interpretar facilmente texto ASCII bruto por não haver gráficos, DOM complexo ou canvas WebGL se rompe nos TUIs modernos
- Frameworks de UI para terminal como Ink (JS/React), Bubble Tea (Go) e tcell tentam melhorar a experiência do desenvolvedor (DX), mas podem criar um ambiente mais hostil para usuários com deficiência visual
- Em muitos casos, TUIs modernos são piores em acessibilidade do que interfaces gráficas mal implementadas
Diferença estrutural entre CLI e TUI
-
CLI: fluxo linear
- CLI funciona com base em
stdin/stdout; você digita um comando, o resultado é adicionado abaixo e o cursor desce - Como a saída é linear e se acumula em ordem cronológica, ela se adapta bem a leitores de tela em nível de kernel como o Speakup
- CLI funciona com base em
-
TUI: grade 2D
- TUI trata a janela do terminal não como um fluxo de texto, mas como uma grade 2D em que cada célula de caractere é usada como se fosse um pixel
- Ao abandonar o fluxo temporal e priorizar o layout espacial, cria-se uma estrutura difícil de acompanhar para leitores de tela
O problema exposto no gemini-cli
- O
gemini-clié uma ferramenta escrita em Node.js com o framework Ink e, por fora, parece apenas uma interface de chat simples - Internamente, o Ink tenta ajustar a árvore de componentes React à grade do terminal
- Ao usar com Speakup (Linux) ou NVDA (Windows), o aplicativo não apenas falha: ele despeja continuamente conteúdo para o leitor de tela ler
-
Uma tela que se comporta como um canvas reativo
- Como o framework trata a tela como um canvas reativo, toda atualização provoca um redesenho
- Quando a IA está “pensando”, para atualizar um timer ou spinner, ele move o cursor de hardware até a posição do timer, escreve o novo tempo e depois volta ao ponto original
- Para usuários videntes, isso passa instantaneamente, mas para usuários de leitores de tela soa como “Responding... Time elapsed 1s... Responding... Time elapsed 2s...” repetidamente
- À medida que o cursor se desloca momentaneamente entre o indicador de status, o spinner e o histórico da conversa, o Speakup tenta ler o que estiver sob o cursor naquele instante
- Como resultado, atualizações do timer e trechos da conversa se misturam, tornando difícil focar no que está sendo digitado de fato
-
Instabilidade com NVDA e colagem
- No Windows, ao abrir um terminal com NVDA, conectar por SSH a uma máquina Linux e entrar em uma sessão
screen, colar texto pode fazer o NVDA travar imediatamente ou deixar o sistema altamente instável - Sempre que um caractere é digitado ou um texto é colado, o estado da aplicação muda, e o framework decide que precisa renderizar a interface novamente
- Se o histórico da conversa faz parte do estado, ele pode tentar redesenhar ou recalcular instantaneamente o layout de milhares de linhas de texto
- Quanto mais mensagens houver na conversa, mais frequentemente esse problema ocorre
- Nem a combinação
Insert+5, usada para evitar anúncios de conteúdo dinâmico, consegue evitar isso
- No Windows, ao abrir um terminal com NVDA, conectar por SSH a uma máquina Linux e entrar em uma sessão
-
O loop de atraso de entrada
- Quando frameworks como Ink rodam em ambientes de thread única como o Node.js, a degradação de desempenho cresce conforme o histórico aumenta
- Ao colar grandes blocos de texto, é preciso calcular diferenças para milhares de linhas
- O sistema fica ocupado calculando como redesenhar a tela, e o processamento da entrada atrasa
- Mesmo ao pressionar uma única tecla, pode ser preciso esperar até 10 segundos para o caractere reaparecer na tela
Por que ferramentas antigas funcionam
- Ferramentas como
nano,vimemenuconfignão são usadas por serem sempre perfeitas em acessibilidade - O ponto central é que elas conseguem ocultar totalmente o cursor ou reduzir o ruído gerado pelo rastreamento da posição do cursor
-
nanoevim: ocultação do cursor- Se o
nanofor executado com opções que exibem a posição do cursor, como--constantshow, ou se ovimfor usado sem determinadas configurações, a usabilidade pode se deteriorar - Quando o cursor fica visível e o rastreamento está ativo, o Speakup prioriza atualizações da posição do cursor em vez do eco de caracteres
- Quando o usuário digita “a”, em vez de ouvir “a”, ele ouve “Column 2”; ao digitar “b”, ouve “Column 3”
- Essas ferramentas antigas podem ser configuradas para suprimir atualizações do cursor visual ou da barra de status, fazendo o leitor de tela depender do fluxo de entrada de caracteres em vez de atualizações de coordenadas
- Frameworks modernos normalmente não oferecem modo “no-cursor” ou “headless” e partem do princípio de que o cursor visual é obrigatório
- Se o
-
menuconfig: foco em coluna única- O
menuconfigdo kernel Linux funciona porque mantém um foco estrito em uma única coluna - Mesmo com bordas e títulos, a área ativa é uma lista vertical, e o cursor permanece preso a essa lista
- O cursor não sai para o canto inferior direito para atualizar um relógio e depois vai ao canto superior esquerdo para atualizar um título
- A complexidade espacial se mantém baixa, então o leitor de tela não perde a referência
- O
-
Irssi: uso de áreas de rolagem
- O Irssi não é acessível por sorte; é uma ferramenta de chat que há mais de 20 anos usa áreas de rolagem do VT100 por meio de um mecanismo de renderização próprio
- Quando chega uma nova mensagem, ele instrui o driver do terminal a “definir da linha 1 à linha 23 como área de rolagem”
- Em seguida, envia o comando de “rolar para cima”, o terminal move o conteúdo para cima e desenha o novo texto na parte inferior dessa área
- Esse método minimiza a interferência com a linha de entrada
- Em vez de reescrever manualmente todos os caracteres da tela, ele depende de recursos de hardware do terminal
- Frameworks modernos ignoram esses recursos de hardware e preferem calcular diferenças de estado da tela para reescrever caracteres, o que custa mais computacionalmente e é hostil à acessibilidade
O problema no tratamento dos issues do gemini-cli
- O Google e os mantenedores do
gemini-cliparecem se importar com acessibilidade, mas regressões importantes de acessibilidade estão sendo deixadas de lado no repositório - Regressões de acessibilidade como a Issue #3435 e a Issue #11305 não têm discussão, roadmap nem correção
- A Issue #1553 foi aberta para rastrear esse tipo de falha de acessibilidade, mas não foi resolvida e acabou fechada automaticamente por um bot
- O bot encerra o issue com uma mensagem genérica dizendo que ele foi fechado por inatividade prolongada e para gerenciamento de backlog
- Fechar relatos de acessibilidade porque os mantenedores não tocaram neles por meses não é organização; é como esconder evidências
- Isso passa o sinal de que, se um bug for ignorado tempo suficiente, ele deixa de existir, enquanto o software continua inutilizável para usuários com deficiência visual
- A métrica de “Closed Issues” do projeto pode melhorar, mas os problemas de acessibilidade seguem sem solução
Conclusão para criar ferramentas de terminal acessíveis
- Se acessibilidade realmente importa em aplicações para terminal, é preciso parar de usar frameworks de UI declarativa que tratam o terminal como um canvas
- As stacks de TUI “modernas” foram otimizadas para facilitar ao desenvolvedor escrever código como se estivesse usando React, sacrificando a capacidade da máquina de renderizar texto com eficiência
- Se a aplicação não garante que o usuário possa ocultar o cursor, ou depende de redesenhos agressivos para mostrar spinners e timers, ela se torna uma ferramenta inacessível
- Para usuários com deficiência visual, um fluxo de CLI simples e linear é muito melhor do que um TUI “inteligente” que atrasa, despeja leitura contínua e espalha o cursor pela tela inteira
2 comentários
Opiniões do Hacker News
Ao ver a UI de renderização do Claude Code, percebi pela primeira vez que um TUI se parece menos com uma interface de linha de comando e mais com um sistema de UI ao estilo DOS ou Borland
Ao investigar a configuração
CLAUDE_CODE_NO_FLICKER=1, ficou claro que esse TUI tem uma estrutura que sobrepõe várias camadas usando códigos de controle do terminalTambém li a implementação de terminal Ink para React: https://github.com/vadimdemedes/ink
Foi interessante ver como isso acaba parecendo WordPerfect ou WordStar antigos, em vez de gráficos baseados em pixels, e do ponto de vista de usuários com deficiência visual a usabilidade é parecida. Ainda assim, lembro que uma linha braille 80x25 para ferramentas de DOS funcionava melhor do que leitores de tela posteriores
Quanto mais olho para os TUIs da moda hoje em dia, pior eles parecem. Dá a impressão de que pegaram todas as piores práticas acumuladas desde o começo da programação e embrulharam tudo numa massa de gelatina difícil de lidar, pesada e lenta, prestes a desabar sob o próprio peso
É a mesma categoria do que se fazia no DOS numa época em que o Windows ainda não era comum ou não era bom o bastante, então não surpreende que seja um desastre ou que tenha sido feito sem entender as capacidades do terminal em que roda
Sempre achei um pouco curioso por que TUI é tão popular. A força do terminal está no modelo de streaming, e utilitários combináveis são uma vantagem muito mais rara em UIs gráficas
Entendo que as limitações do terminal podem fazer o design de um TUI focar mais no propósito da ferramenta do que em enfeites, mas pessoalmente isso não me parece um motivo tão convincente
Já existe um grupo considerável de engenheiros que vive no terminal usando Vim/Neovim/tmux/zellij etc., e muito trabalho de desenvolvimento já acontece executando scripts no terminal, então há demanda para levar o máximo possível de tarefas para lá
Em plataformas principais como macOS e Linux, a distribuição via gerenciadores de pacotes está praticamente resolvida, enquanto a distribuição de apps nativos multiplataforma continua fragmentada
Por causa dessa demanda e dessas vantagens de distribuição, componentes de TUI como Ink para React e Bubble Tea para Go melhoraram bastante, e Electron carrega a imagem de apps lentos e inchados, algo que desenvolvedores tendem a evitar
No fim, produtos bem-sucedidos às vezes migram para outros formatos, como o Claude Code levando ao Claude Cowork e o OpenCode ganhando o OpenCode Web, mas é mais fácil testar rapidamente a adequação produto-mercado de ferramentas para desenvolvedores com um TUI, e muitos usuários continuarão no terminal mesmo depois que outras UIs forem lançadas
Também poderia ter sido facilmente imitado numa UI gráfica, mas os atalhos de teclado acabaram ficando para depois
Ainda encontro usuários que sentem falta desse tipo de fluxo de trabalho antigo, e eles o descrevem como “interface de texto antiga”, ou seja, TUI. Mas, ouvindo com atenção, o que eles realmente querem é um fluxo quase instantâneo e centrado em atalhos. Ao inserir dados, o importante é velocidade, não animação
Iniciantes gostam de firula visual, mas usuários experientes já não ligam para isso
Graças às limitações impostas pelo terminal, os apps em geral se parecem e se comportam de forma parecida. No mundo de fora isso costuma ser ignorado no dia a dia, embora padrões de experiência do usuário sejam possíveis; o TUI, por assim dizer, fica no ponto ótimo de menor surpresa
Graças às ferramentas TUI, consegui montar uma IDE invisível sob medida para mim quando preciso. Com guake e yakuake ela pode ser ocultada, e com zellij fica organizada por projeto e por aba. Para um trabalho com vários papéis misturados como o meu, isso encaixa perfeitamente
Dito isso, acho que ninguém chamaria isso de sofisticado
Do ponto de vista de quem mantém um projeto, pode ser diferente. As pessoas que possuem e mantêm o projeto podem definir o significado de Open e Closed nas issues, e o usuário não precisa necessariamente concordar
Por exemplo, Open pode significar “ainda não classificado ou em trabalho ativo”, e Closed pode significar “vimos este ticket, mas por enquanto ninguém da equipe vai trabalhar nele”
Ou seja, Closed não precisa necessariamente significar “foi rejeitado e esta decisão é final”; pode significar “não é algo em que estamos trabalhando neste momento”. Pode não ser intuitivo para todo mundo, mas se isso ajuda os membros do projeto a organizar a carga de trabalho, dá para justificar
O Google em geral cuida bem de acessibilidade e publica relatórios de conformidade para a maioria dos produtos: https://belonging.google/accessibility-conformance-reports/
Concordo com essa avaliação. Indo além, teria sido muito mais fácil se os desenvolvedores tivessem seguido e padronizado melhor o CUA, isto é, o padrão de interface Common User Access da IBM, que já era satisfatório o bastante: https://en.wikipedia.org/wiki/IBM_Common_User_Access
Se os desenvolvedores quiserem experimentar diferentes composições de UI, tudo bem, mas bastaria manter por trás um CUA que pudesse ser invocado tanto por máquinas quanto por pessoas. Infelizmente, ergonomia nunca foi o ponto forte dos desenvolvedores
Esse é um problema bem documentado entre TUI e Windows
Nos anos 90, quando a maioria dos sistemas SAP migrou de terminais AS/400 para Windows NT, muita gente disse que a produtividade caiu bastante
Nunca trabalhei na SAP, mas minha mãe trabalhou, e o fluxo de trabalho, que era totalmente em forma de tabela e baseado em teclas de função, virou algo de pegar o mouse, mover e clicar muito. Em várias funções, navegação por tabulação e teclas F desapareceram
Ela me mostrou como atravessava o sistema inteiro numa velocidade absurda só com
ESC ESC F4 F3 TAB TAB, e aquilo nem era um sistema propriamente dito, era um terminalResumindo: apps baseados em Windows são bons para descoberta e para novos usuários, enquanto apps baseados em terminal são mais rápidos e melhores para navegação baseada em memória e para usuários avançados
Na prática, todos os apps bem-sucedidos voltados a especialistas ou usuários avançados são feitos com esses caminhos rápidos em mente. Até a faixa de opções da Microsoft, que recebe muito ódio sem motivo, é um exemplo disso. Dá para operar pelo teclado, personalizar e, ao mesmo tempo, ela continua sendo descobrível
A UI gráfica apenas relaxa as restrições, e por isso fica mais fácil estragar a experiência do usuário
TUI deveria ser originalmente uma opção simples, mas agora virou um aplicativo web fantasiado de terminal
Está longe de um app web e é muito pior na maioria dos eixos
Sobre o mito de que “como é texto, é acessível”: não, não é. Ninguém acredita nisso
Quando desenvolvedores dizem isso, estão falando de documentos de texto e, com ressalvas, de apps de terminal de comando único como grep, cut e ls. É difícil dizer que isso tenha sido dito sobre TUI
O problema não é usar um framework de UI declarativa em si, mas sim o motor de renderização produzido por esses frameworks não levar acessibilidade em conta
Mas parece que ninguém se importou com isso, e tudo ficou só no “vamos deixar o terminal bonito”
TUI será redescoberto sem elementos como janelas. Ainda são poucos os que percebem isso, mas talvez ele acabe sendo baseado em sprites, como um console C64. Pode ser que alguém já esteja fazendo isso em algum lugar
Janelas e painéis já podem ser deixados para trás. Um ambiente de texto calmo continua fazendo parte do DNA cultural da humanidade em 2026, e esse meio ainda foi explorado muito menos do que poderia
A agressividade cognitiva da web moderna, na verdade, me parece muito mais próxima de um pesadelo, num sentido fortemente alucinatório em que imagens, vídeos e anúncios se misturam sem contexto
Comentários no Lobste.rs
Este texto, como muitos outros posts de blog, tem um forte cheiro de texto escrito com ajuda de IA
LLMs adoram títulos desse tipo: “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”
Poderia ter sido um excelente post de blog, mas se parece que o autor jogou um esboço no ChatGPT e parou por aí, isso é ruim tanto para o leitor quanto para o autor
Chamar um problema muito específico e pontual de problema “clássico” é a mesma coisa
Isso é realmente deprimente. Em resumo, existem TUIs acessíveis como o Irssi, mas os frameworks modernos de TUI ignoram esses precedentes e dependem de diff de grade e movimentação do cursor
Leitores de tela leem o conteúdo da posição para a qual o cursor se move, então o resultado vira uma bagunça ou gera uma quantidade absurda de spam de leitura
Fico em dúvida se a explicação técnica aqui está totalmente correta
Em especial, o Ink por muito tempo não suportou renderização incremental nenhuma, e a maioria dos apps que usam Ink ainda não a ativa. E mesmo essa renderização incremental é baseada em linhas, então ela não move o cursor até a posição real do timer
O Gemini CLI exige uso de buffer alternativo para ativar a renderização incremental, e isso é desativado quando o modo interno compatível com leitor de tela está ligado. A documentação da opção relacionada está aqui
Além disso, rich/textual em Python muitas vezes é bem mais rápido que Ink, mesmo rodando sobre uma linguagem mais lenta e em geral de thread única. Fazer diff de milhares de linhas não é necessariamente tão lento assim, e certamente não a ponto de levar 10 segundos
Não duvido que a experiência de usuário esteja frustrante e quebrada, mas a causa exata apresentada parece possivelmente ser uma alucinação de LLM ou baseada em informação incompleta. A renderização incremental do Ink, mesmo quando ativada, não funciona como foi descrito
Na prática, é bem provável que o redesenho da tela inteira confunda leitores de tela, e que o redesenho baseado em linhas seja ruim por fazer reler fragmentos arbitrários e quebrados de texto sem relação com a mudança
Culpar só a TUI não é justo
O problema real é que o suporte a acessibilidade de quase toda a pilha é péssimo
Primeiro, a maioria dos emuladores de terminal com renderização por GPU não usa nem um pouco as APIs de acessibilidade fornecidas pelo sistema. Quando o texto é renderizado pela GPU, ferramentas de acessibilidade não conseguem “ler”; ele parece só uma imagem. Kitty, Alacritty e WezTerm entram nisso. Meu terminal, Ghostty, pode ser lido pela API de acessibilidade no macOS, e iTerm2 e Terminal.app também
Segundo, não existe nenhuma sequência de terminal nem movimento padronizado para uma TUI transmitir informação de acessibilidade ao emulador de terminal. Precisaríamos de algo equivalente a anotações ao estilo ARIA para células do terminal, intervalos de execução e regiões, mas não há nenhuma iniciativa disso. Mesmo que a TUI lide bem com o cursor, ainda vai dar problema em muitos casos de uso
Por exemplo, no Ghostty temos trabalhado para integrar OSC133 com a API de acessibilidade, expondo cada prompt do shell, entrada e comando como elementos estruturalmente significativos, e não apenas caixas de texto simples. Isso mostra que especificação de terminal, TUI e emulador de terminal precisam se encaixar juntos
A pilha inteira está apodrecida, e quase ninguém quer consertar isso de verdade. Eu também só faço o melhor que posso com tempo limitado, mas este é um tema enorme que ainda exige política de ecossistema, então é difícil de enfrentar
Como bônus, a realidade ao mesmo tempo interessante e terrível é que a IA está ajudando a melhorar a acessibilidade aqui. Muitas ferramentas de IA usam ou abusam das APIs de acessibilidade para ler listas de janelas e realizar entrada. Então mais aplicativos começaram a levar integração de acessibilidade muito mais a sério por causa dos casos de uso de IA
Fico irritado todos os dias porque o Claude Code e o gemini-cli não são baseados em readline
Eles colocaram algumas teclas parecidas, mas falta a longa cauda dos atalhos readline já familiares
A Anthropic poderia admitir que decidir “fazer como desenvolvimento web” foi um erro e recomeçar com readline
A ideia de que a experiência familiar de desenvolvimento para quem cria essas ferramentas é mais importante do que a experiência familiar de uso para quem as usa está errada
Na prática também quase não existem soluções de terceiros famosas e bem mantidas. Se você precisa de uma caixa de entrada flexível, acaba tendo de construir do zero
Em contraste com o excelente widget Input do Textual ou com outra biblioteca do ecossistema JS, o OpenTUI
Eu não gosto de LLMs, então pessoalmente considero a UI ruim uma vantagem, mas pode haver um motivo para não usarem readline
Editores de terminal como kakoune e helix provavelmente teriam dificuldade para passar em critérios de acessibilidade, a menos que usem o truque de “esconder o cursor”
Ainda assim, provavelmente não seriam tão acessíveis quanto o VS Code
Fora o VS Code, que IDE-lite ou IDE multiplataforma acessível existe? Não gosto da postura cada vez mais hostil do VS Code. Talvez os IDEs da JetBrains
O lado ruim é que, embora o Emacs em si seja multiplataforma, o emacspeak pode ter uma dependência fraca de Linux por causa de TTS. Ou talvez não. Nunca tentei no Windows
Se for acessibilidade para pessoas cegas, você precisa de emacspeak ou das ferramentas de acessibilidade para deficiência visual da própria plataforma
Acessibilidade é um espectro, não uma caixa de seleção
O Links tem um modo de terminal braille separado, que troca elementos de GUI falsos por um menu de tela cheia mais simples e também muda a navegação por setas para ser linha a linha
Outro caso interessante é o edbrowse. É um navegador em modo texto criado por Karl Dahlke, que é cego, e ao contrário de navegadores web em modo texto mais populares, ele não usa TUI, e sim uma interface de linha de comando no estilo do ed
Se for o framework Ink, isso provavelmente explica por que a CLI usa 100% da CPU, trava para sempre e continua redesenhando um histórico longo de chat. Uma pena