Skip to content

A screen reader is never told the Tuval composer is off inside a subagent view #8635

Description

@usirin

Summary

While the Tuval chat window's view slot shows a subagent, the composer is disabled and the reason
lives in the field's placeholder. A disabled textarea is not focusable and not in the tab order, so
that placeholder is visual-only — a screen-reader operator reaches nothing where the composer was
and is told nothing about why, or where prompts go instead.

What I was doing

Reviewing PR #8629 (the review-code verdict on issue #8466) at head
89f7ce562b38431598eb097f678b17106c87c898. The PR passes on its stated criteria; this is a gap the
criteria did not ask about.

What I observed

apps/tuval/src/shell/chat/ChatWindow.tsx passes disabled={viewing !== null} to
AgentChatInput, and swaps the DesignTranslationProvider's translate so the placeholder reads
subagentViewPlaceholder from apps/tuval/src/shell/chat/copy.ts:

"Prompts go to the main agent — go back to its transcript to send (the row above, or Escape)."

packages/design/src/AgentChatInput.tsx puts a real disabled attribute on the textarea and on the
submit button, so both leave the tab order. The window's one live region — the
role="status" paragraph in ChatWindow.tsx (around line 993) — announces only the transcript
swap: "Showing the reviewer subagent's transcript. Still running." It says nothing about the
composer being off, and nothing about where a prompt would go.

So the whole explanation of the disabled state is carried by an accessible name a screen-reader user
cannot reach.

Why it matters

The point of the founder's ruling on #8466 was to stop a prompt landing in a transcript nobody is
reading, and to tell the operator where it should go instead. A screen-reader operator gets the
first half and not the second: the composer silently vanishes from the tab order with no
explanation. Might read as the app breaking rather than as a deliberate state. Cost of leaving it is
bounded — one operator class, one window — but this is the surface where a "why is it gone" question
is most expensive to answer without sight.

Pointers

Suggested next step (non-binding)

A guess, two shapes. Either fold the composer's state into the existing status announcement, so the
swap announces both the transcript and where prompts now go; or switch to aria-disabled plus a
no-op submit, keeping the field focusable so its accessible description can carry the hint. The
second matches what #8466's own criterion 1 offered as the alternative.


Filed by an agent · session 425cb3d4-b7c2-4bc3-b562-0776c88e7787 · 2026-09-08T21:12:15Z

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions