Skip to content

rfc: accessible mode #585

Description

@dreyfus92

coming out of the discord convo about spinners and screen readers. tldr of the problem: spinners and live-redrawn prompts get re-announced on every repaint by voiceover/nvda/orca, which makes clack prompts effectively unusable with assistive tech. github hit the same wall with their cli and shipped static text + numbered inputs instead: https://github.blog/engineering/user-experience/building-a-more-accessible-github-cli/

detecting screen readers isn't viable (the reader runs client-side, so over ssh/containers there's zero signal, and local platform APIs are inconsistent + amount to sniffing disability status). so the proposal is an explicit opt-in following the emerging ACCESSIBLE env var convention (charmbracelet/huh already honors it in go land). nothing in the js prompt ecosystem does this yet, so clack adopting it likely sets the convention.

this issue is the coordination doc: design + checklist live here, chatter can stay on discord.

activation semantics

accessible mode is enabled when any of these resolve true, in priority order:

  1. explicit accessible: true option on a prompt (or global setting)
  2. ACCESSIBLE env var set to any non-empty value (0 and false explicitly disable)
  3. open question: should !isTTY imply it, or stay a separate degradation path?

behavior per prompt

prompt accessible behavior
spinner print message once, plain still working: <msg> line every 30-60s (configurable), final done/error line. zero redraws
progress-bar log at milestones (25/50/75/100) instead of continuous repaint
task / task-log / stream audit for line rewrites, route through same static path
select / select-key numbered list, read a number from stdin, re-prompt on invalid
multi-select / group-multi-select numbered list, comma-separated numbers in, echo back parsed selection
limit-options no pagination, print full list
autocomplete degrade to plain "type your answer" + printed options (may split to follow-up)
text / confirm / password / path / multi-line / date strip cursor-repositioning decorations, plain question in / plain input out, confirm becomes (y/n)
note / box / log / messages plain text with clear begin/end delimiters, no box-drawing repaints

implementation approach

intent is for accessible mode to be a distinct render path rather than if (accessible) conditionals scattered through every prompt. all prompts already flow through the shared render layer in core, so the accessible output should hang off that seam. keeping it isolated there also means future renderer work inherits it for free.

proposed plan

  • isAccessible() util in @clack/core + resolution order tests
  • thread accessible option through shared prompt options in core/src/types.ts
  • baseline screen reader recording against current spinner + select (before/after material)
  • pr 1: spinner, progress-bar, task/task-log/stream (static output path)
  • pr 2: select, select-key, multi-select, group-multi-select, limit-options
  • follow-up: autocomplete
  • pr 3: cleanup pass on text/confirm/password/path/multi-line/date + note/box/log/messages
  • validation session with a screen reader user on a build
  • docs page + accessible example in examples dir

open questions

  • !isTTY implies accessible mode, or keep as separate degradation?
  • global setting api shape (per-prompt option only vs updateSettings-style global)
  • interval default for spinner status lines (30s? 60s?)
  • how custom renderers compose with accessible mode

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    Needs triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions