Add --accessible flag for screen reader support#16
Add --accessible flag for screen reader support#16taylorarndt wants to merge 1 commit intomicrosoft:mainfrom
Conversation
When enabled (via --accessible flag or INK_SCREEN_READER=true env var), the TUI removes box-drawing border characters, replaces the block-art banner with plain text, and substitutes Unicode symbols with text equivalents (e.g. checkmark -> PASS, bullet -> [x]). This prevents screen readers from reading every border character aloud as "box drawing light arc down and right" etc. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds an opt-in accessibility mode for Primer’s Ink-based TUIs to improve screen reader output by removing box-drawing borders, replacing banner art, and substituting certain Unicode symbols with text.
Changes:
- Introduces a global
--accessibleCLI flag and readsINK_SCREEN_READER=trueas an alternative. - Updates
tuiandbatchcommands to pass accessibility settings into Inkrender()and the UI components. - Adjusts TUI components to remove borders and replace some Unicode glyphs with text when
accessibleis enabled.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
src/cli.ts |
Adds global --accessible option (and touches CLI version line). |
src/commands/tui.tsx |
Derives accessible and passes it to Ink render + PrimerTui. |
src/commands/batch.tsx |
Derives accessible and passes it to Ink render + BatchTui. |
src/ui/AnimatedBanner.tsx |
Makes StaticBanner render plain “PRIMER” text when accessible. |
src/ui/tui.tsx |
Plumbs accessible through the main TUI and adjusts borders/verdict glyphs. |
src/ui/BatchTui.tsx |
Adds accessible prop and replaces some list glyphs + borders accordingly. |
| const accessible = cmd.parent?.opts().accessible || process.env.INK_SCREEN_READER === "true"; | ||
| const { waitUntilExit } = render( | ||
| <BatchTui token={token} outputPath={options.output} /> | ||
| <BatchTui token={token} outputPath={options.output} accessible={accessible} />, | ||
| { isScreenReaderEnabled: accessible } |
There was a problem hiding this comment.
batchCommand dereferences cmd.parent without guarding cmd itself. If the Command instance is not passed into the action handler (depending on Commander configuration), this will crash primer batch at startup. Consider making cmd optional (cmd?.parent?.opts()) and/or enabling passCommandToAction(true) when registering commands.
| .version("0.1.0") | ||
| .option("--accessible", "Enable screen reader friendly output (removes box-drawing characters, replaces Unicode symbols with text)"); |
There was a problem hiding this comment.
The CLI version is hardcoded as 0.1.0, but package.json is 1.0.0. This can cause confusing/mismatched primer --version output. Consider sourcing the version from package.json (or keeping these in sync).
| .description("Prime repositories for AI-assisted development") | ||
| .version("0.1.0"); | ||
| .version("0.1.0") | ||
| .option("--accessible", "Enable screen reader friendly output (removes box-drawing characters, replaces Unicode symbols with text)"); |
There was a problem hiding this comment.
--accessible is added only on the root command. With Commander’s default parsing rules, primer tui --accessible will be treated as an unknown option on the tui subcommand (users must write primer --accessible tui). If you want it to behave like a true global flag regardless of position, consider also adding the option to subcommands (or enabling positional/pass-through options).
| <Text color={repo.hasInstructions ? "green" : "red"}>{repo.hasInstructions ? "✓" : "✗"} </Text> | ||
| <Text color={isCursor ? "cyan" : undefined}>{isCursor ? (accessible ? "> " : "❯ ") : " "}</Text> | ||
| <Text color={isSelected ? "green" : "gray"}>{isSelected ? (accessible ? "[x]" : "◉") : (accessible ? "[ ]" : "○")} </Text> | ||
| <Text color={repo.hasInstructions ? "green" : "red"}>{repo.hasInstructions ? (accessible ? "HAS" : "✓") : (accessible ? "NEEDS" : "✗")} </Text> |
There was a problem hiding this comment.
In accessible mode, the repo status indicator uses very terse labels (HAS / NEEDS). For screen reader output this can be ambiguous (has/needs what?). Consider using more explicit text like HAS_INSTRUCTIONS / NEEDS_INSTRUCTIONS (or similar) so the meaning is clear when read aloud.
| <Text color={repo.hasInstructions ? "green" : "red"}>{repo.hasInstructions ? (accessible ? "HAS" : "✓") : (accessible ? "NEEDS" : "✗")} </Text> | |
| <Text color={repo.hasInstructions ? "green" : "red"}>{repo.hasInstructions ? (accessible ? "HAS_INSTRUCTIONS" : "✓") : (accessible ? "NEEDS_INSTRUCTIONS" : "✗")} </Text> |
| {evalResults.map((r) => ( | ||
| <Text key={r.id} color={r.verdict === "pass" ? "green" : r.verdict === "fail" ? "red" : "yellow"}> | ||
| {r.verdict === "pass" ? "✓" : r.verdict === "fail" ? "✗" : "?"} {r.id}: {r.verdict} (score: {r.score}) | ||
| {r.verdict === "pass" ? (accessible ? "PASS" : "✓") : r.verdict === "fail" ? (accessible ? "FAIL" : "✗") : "?"} {r.id}: {r.verdict} (score: {r.score}) |
There was a problem hiding this comment.
In accessible mode, the verdict line prints both the text label and the verdict value (e.g., PASS <id>: pass ... / FAIL <id>: fail ...), which is redundant when read aloud. Consider outputting a single normalized label (e.g., Verdict: PASS) or omitting r.verdict when the accessible label is used.
| {r.verdict === "pass" ? (accessible ? "PASS" : "✓") : r.verdict === "fail" ? (accessible ? "FAIL" : "✗") : "?"} {r.id}: {r.verdict} (score: {r.score}) | |
| {accessible | |
| ? `Verdict: ${r.verdict === "pass" ? "PASS" : r.verdict === "fail" ? "FAIL" : "UNKNOWN"} ${r.id} (score: ${r.score})` | |
| : `${r.verdict === "pass" ? "✓" : r.verdict === "fail" ? "✗" : "?"} ${r.id}: ${r.verdict} (score: ${r.score})`} |
| const accessible = cmd.parent?.opts().accessible || process.env.INK_SCREEN_READER === "true"; | ||
| const { waitUntilExit } = render( | ||
| <PrimerTui repoPath={repoPath} skipAnimation={skipAnimation || accessible} accessible={accessible} />, | ||
| { isScreenReaderEnabled: accessible } |
There was a problem hiding this comment.
tuiCommand assumes Commander passes a cmd argument, but cmd is dereferenced unguarded (cmd.parent...). If Commander doesn’t pass the Command instance (depending on configuration), this will throw at runtime and break primer tui. Make cmd optional (use cmd?.parent?.opts()) and/or enable passCommandToAction(true) when wiring commands so the Command instance is reliably available.
chore: update dependencies across root and vscode-extension
Why
I am a blind developer. When I run the Primer TUI with a screen reader, every box-drawing border character gets read aloud individually: "box drawing light arc down and right, box drawing light horizontal, box drawing light horizontal..." over and over. The block-art ASCII banner is even worse. Unicode symbols like checkmarks and bullets also get verbose announcements instead of useful information.
This makes the TUI unusable with a screen reader.
What this does
Adds a
--accessibleflag that strips out all the visual decoration and replaces it with plain text that screen readers can actually read.Borders: Removed when
--accessibleis on. No more box-drawing characters.Banner: The block-art "PRIMER" banner becomes the word "PRIMER" in plain text.
Symbols: Unicode gets replaced with text:
✓becomesPASSorOK✗becomesFAIL◉becomes[x]○becomes[ ]❯becomes>→becomes-Ink integration: Passes
isScreenReaderEnabled: trueto Ink'srender()so the framework itself also optimizes output.Usage
What a screen reader hears now
Before:
After:
Files changed
src/cli.ts— global--accessibleoptionsrc/commands/tui.tsx— reads flag, passes to render and componentsrc/commands/batch.tsx— samesrc/ui/AnimatedBanner.tsx— plain text banner in accessible modesrc/ui/tui.tsx— conditional borders and text symbolssrc/ui/BatchTui.tsx— conditional borders and text symbols for all listsTest plan
primer --accessible tui— no box-drawing characters in outputprimer --accessible batch— no Unicode symbols in listsINK_SCREEN_READER=true primer tui— same behavior via env varprimer tuiwithout flag — normal visual output unchanged🤖 Generated with Claude Code