Skip to content

fix(ui): associate labels with their form controls in owner/playground/maintainer-settings/digest panels #7532

Description

@JSONbored

Context

Several of this app's own panels correctly associate a <label> with its control by nesting the
control inside the label (e.g. commands-panel.tsx lines 91-102, maintainer-settings.tsx's
FieldGroup/select/number fields at lines 437-474 — <label className="block"><span>…</span><input/></label>).
That pattern gives the input an accessible name implicitly, with no htmlFor/id needed, and is fine.

Four places in the four files below do not follow that pattern, or have no <label> at all:

  1. owner-panel.tsx lines 85-94 — the "Repository" <label> is a sibling of the <Input>, not a
    wrapper, and neither carries htmlFor/id:

    <label className="font-mono text-token-2xs uppercase tracking-wider text-muted-foreground">
      Repository
    </label>
    <Input value={repo} onChange={(e) => setRepo(e.target.value)} ... placeholder="owner/repo" />

    A screen reader gets no accessible name for this input at all (placeholder text is not a reliable
    substitute for a programmatic label — WCAG 3.3.2/4.1.2).

  2. playground-panel.tsx lines 216-249 — the same sibling-label pattern, three times over: the "Tool"
    <select>, the "Repo" <input>, and the "Branch" <input> all have a <label> immediately preceding
    them with no htmlFor/id and no nesting.

  3. maintainer-settings.tsx's FocusManifestEditor — the JSON focus-manifest <textarea> (an
    editable, functionally significant control — it edits real repo config) has no label at all, only a
    preceding <h3>/<p> that is not programmatically linked via aria-labelledby, htmlFor, or aria-label.

  4. digest-panel.tsx's SubscribeForm — the email <Input type="email"> has only a placeholder and
    nearby (sibling, unassociated) descriptive text ("Store-only digest" / "Persist a subscription record…"),
    no <label>/aria-label.

Confirmed the shared Input primitive (packages/loopover-ui-kit/src/components/input.tsx) is a thin
wrapper around a native <input> with no automatic aria-label/placeholder-as-name behavior, so these four
controls genuinely have no accessible name today — this is code-verifiable, not a subjective design call.
None of the four files has any accessibility-focused test today (maintainer-settings.tsx,
owner-panel.tsx, playground-panel.tsx, and digest-panel.tsx have no .test.tsx file at all).

Requirements

  • owner-panel.tsx's Repository input: either nest the <Input> inside the <label> (matching the
    pattern already used in commands-panel.tsx/maintainer-settings.tsx), or give the label htmlFor and
    the input a matching id.
  • playground-panel.tsx's Tool/Repo/Branch controls: same fix, applied to all three.
  • maintainer-settings.tsx's focus-manifest <textarea>: add either a visually-hidden <label htmlFor=…>
    tied to an id on the textarea, or an aria-label/aria-labelledby pointing at the existing <h3>
    heading — any one is acceptable, the requirement is that the textarea has a non-empty accessible name.
  • digest-panel.tsx's email input: add a <label> (nested or htmlFor/id-linked) or an aria-label
    (e.g. aria-label="Digest notification email") — a bare placeholder does not satisfy this.
  • Do not change any visual layout, spacing, or styling — this is a markup-association fix only, not a
    redesign. (Per this repo's UI rule, any change that alters visible output requires a screenshot table and
    manual review regardless; if the fix can be done with zero visual diff — e.g. htmlFor/id addition, or
    a visually-hidden label — prefer that so the change stays in the "no visual change" lane and follows the
    normal gate instead of forced manual review.)

Deliverables

  • owner-panel.tsx — Repository input has a programmatically associated label.
  • playground-panel.tsx — Tool/Repo/Branch controls each have a programmatically associated label.
  • maintainer-settings.tsx — the focus-manifest <textarea> has a non-empty accessible name.
  • digest-panel.tsx — the subscribe-email input has a non-empty accessible name.
  • A regression test (new or added to an existing suite) asserting each control is queryable by accessible
    name/role (e.g. Testing Library's getByRole("textbox", { name: /.../ }) / getByLabelText) for all
    four fixed controls.

Test Coverage Requirements

apps/loopover-ui/** is outside the Codecov coverage.include set (UI code, not src/**/workers/**), so
the 99% patch gate does not apply, but each of these four files currently has zero tests — add at least one
test per file asserting the fixed control is reachable via its accessible name/role, so the fix is
regression-proof and not just visually-inspected.

Expected Outcome

All four controls are reachable by screen readers and testing-library queries via their accessible name;
getByLabelText/getByRole(..., { name }) finds each control without falling back to placeholder text.

Links & Resources

  • apps/loopover-ui/src/components/site/app-panels/owner-panel.tsx lines 85-94
  • apps/loopover-ui/src/components/site/app-panels/playground-panel.tsx lines 213-250
  • apps/loopover-ui/src/components/site/app-panels/maintainer-settings.tsx (FocusManifestEditor,
    <textarea> around lines 575-582)
  • apps/loopover-ui/src/components/site/app-panels/digest-panel.tsx (SubscribeForm, <Input> around lines
    216-224)
  • Correct existing pattern to mirror: apps/loopover-ui/src/components/site/app-panels/commands-panel.tsx
    lines 91-102, maintainer-settings.tsx's own FieldGroup select/number fields (lines 437-474)
  • Shared primitive: packages/loopover-ui-kit/src/components/input.tsx (confirms no automatic
    accessible-name behavior)

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.help wantedExtra attention is needed

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions