Context
ChatComposer (apps/loopover-miner-ui/src/components/chat-composer.tsx) renders the chat message
input as:
<Textarea
ref={textareaRef}
value={value}
onChange={...}
onKeyDown={...}
placeholder={placeholder}
disabled={disabled}
rows={1}
className="max-h-[160px] resize-none"
/>
with no aria-label, aria-labelledby, or wrapping <label>. packages/loopover-ui-kit/src/components/textarea.tsx
(the shared primitive) is a deliberately bare wrapper around a native <textarea> — it injects no
label of its own, by design, so the caller is expected to supply one. placeholder text is not a
substitute for a programmatic accessible name (WCAG 2.1 SC 3.3.2 "Labels or Instructions" and SC 4.1.2
"Name, Role, Value"): it disappears once the user has typed anything, and its exposure to assistive
technology as an accessible name is inconsistent across browsers/screen readers, unlike aria-label.
Every other bare/icon-only interactive control audited in this codebase (theme-toggle.tsx,
npm-install.tsx's copy button, back-to-top.tsx, health-dot.tsx, mcp-version-badge.tsx)
correctly sets an explicit aria-label — this composer is the one verified exception. The existing
test file confirms this: chat-composer.test.tsx line 12 queries the textarea with the bare
screen.getByRole("textbox") (no { name: ... } option), which is exactly what you'd expect from a
control that has no accessible name to query by.
Requirements
- Add an
aria-label to the <Textarea> in ChatComposer describing its purpose for a screen-reader
user (e.g. "Chat message" or similar — should read sensibly standing alone, not duplicate the
visible placeholder verbatim if that would be redundant when both are announced together in any
given screen reader).
- The accessible name must be present regardless of the
placeholder prop's value (i.e. don't derive
the aria-label from placeholder, since callers can override placeholder and the label
shouldn't silently follow along as user-facing hint copy changes) — hardcode a stable, descriptive
label appropriate to what this component always is (a chat message composer), matching how
theme-toggle.tsx's aria-label is a fixed, purpose-describing string rather than derived from
visible copy.
Deliverables
Test Coverage Requirements
apps/loopover-miner-ui/** is apps/**-only UI work and outside Codecov's coverage.include/
ignore: apps/** gate, so no patch-coverage percentage applies — but a regression test is still
required per this repo's own discipline. Update
apps/loopover-miner-ui/src/chat-composer.test.tsx's existing getByRole("textbox") query (line 12)
to screen.getByRole("textbox", { name: /<the chosen label text>/i }), so the test actually asserts
an accessible name exists and pins the specific fix instead of only checking the control's role.
Expected Outcome
A screen-reader user navigating to the miner dashboard's chat composer hears a real, stable name for
the message input (not just its placeholder, which disappears once text is entered), matching the
accessible-naming standard already applied to every other interactive control audited in this pass.
Links & Resources
apps/loopover-miner-ui/src/components/chat-composer.tsx:57-75
apps/loopover-miner-ui/src/chat-composer.test.tsx:12 (query to update)
apps/loopover-miner-ui/src/components/theme-toggle.tsx:51 (existing aria-label precedent in the same app)
Context
ChatComposer(apps/loopover-miner-ui/src/components/chat-composer.tsx) renders the chat messageinput as:
with no
aria-label,aria-labelledby, or wrapping<label>.packages/loopover-ui-kit/src/components/textarea.tsx(the shared primitive) is a deliberately bare wrapper around a native
<textarea>— it injects nolabel of its own, by design, so the caller is expected to supply one.
placeholdertext is not asubstitute for a programmatic accessible name (WCAG 2.1 SC 3.3.2 "Labels or Instructions" and SC 4.1.2
"Name, Role, Value"): it disappears once the user has typed anything, and its exposure to assistive
technology as an accessible name is inconsistent across browsers/screen readers, unlike
aria-label.Every other bare/icon-only interactive control audited in this codebase (
theme-toggle.tsx,npm-install.tsx's copy button,back-to-top.tsx,health-dot.tsx,mcp-version-badge.tsx)correctly sets an explicit
aria-label— this composer is the one verified exception. The existingtest file confirms this:
chat-composer.test.tsxline 12 queries the textarea with the barescreen.getByRole("textbox")(no{ name: ... }option), which is exactly what you'd expect from acontrol that has no accessible name to query by.
Requirements
aria-labelto the<Textarea>inChatComposerdescribing its purpose for a screen-readeruser (e.g.
"Chat message"or similar — should read sensibly standing alone, not duplicate thevisible
placeholderverbatim if that would be redundant when both are announced together in anygiven screen reader).
placeholderprop's value (i.e. don't derivethe
aria-labelfromplaceholder, since callers can overrideplaceholderand the labelshouldn't silently follow along as user-facing hint copy changes) — hardcode a stable, descriptive
label appropriate to what this component always is (a chat message composer), matching how
theme-toggle.tsx'saria-labelis a fixed, purpose-describing string rather than derived fromvisible copy.
Deliverables
ChatComposer's<Textarea>has a stablearia-label(or equivalentaria-labelledby) givingit a real accessible name.
Test Coverage Requirements
apps/loopover-miner-ui/**isapps/**-only UI work and outside Codecov'scoverage.include/ignore: apps/**gate, so no patch-coverage percentage applies — but a regression test is stillrequired per this repo's own discipline. Update
apps/loopover-miner-ui/src/chat-composer.test.tsx's existinggetByRole("textbox")query (line 12)to
screen.getByRole("textbox", { name: /<the chosen label text>/i }), so the test actually assertsan accessible name exists and pins the specific fix instead of only checking the control's role.
Expected Outcome
A screen-reader user navigating to the miner dashboard's chat composer hears a real, stable name for
the message input (not just its placeholder, which disappears once text is entered), matching the
accessible-naming standard already applied to every other interactive control audited in this pass.
Links & Resources
apps/loopover-miner-ui/src/components/chat-composer.tsx:57-75apps/loopover-miner-ui/src/chat-composer.test.tsx:12(query to update)apps/loopover-miner-ui/src/components/theme-toggle.tsx:51(existingaria-labelprecedent in the same app)