Found while building the 6.14.9 epic-privacy E2E (MOTIR-439).
The epic-privacy toggle on the issue-detail page has NO accessible name — a WCAG 2.1 §4.1.2 (Name, Role, Value) defect on a form control.
Root cause (rung-2, shipped code):
app/(authed)/issues/[key]/_components/EpicPrivacyControl.tsx names its switch via aria-labelledby={labelId} (the id of the visible "Make this epic private" label span).components/ui/Switch.tsx primitive only accepts an aria-label prop — it destructures a FIXED prop set and does NOT forward aria-labelledby (or any other aria-*) to the underlying role="switch" button.aria-labelledby is silently dropped, so the rendered switch is role=switch with an EMPTY accessible name. Confirmed in the Playwright a11y snapshot: the node is bare - switch (no name), with the label only present as adjacent text.Impact: a screen-reader user hears an unnamed toggle; getByRole("switch", { name }) cannot target it (the E2E falls back to the page-unique getByRole("switch")).
Fix options:
aria-labelledby?: string to SwitchProps and forward it to the button (mirrors the existing aria-label pass-through) — then EpicPrivacyControl works as written; OREpicPrivacyControl, pass aria-label={t("epicPrivacyLabel")} to the Switch (the prop the primitive already supports) instead of aria-labelledby.Option 1 is the more general fix (other call sites may want label association). Add a vitest/RTL assertion that the toggle has an accessible name, and the 6.14.9 E2E can then re-tighten to getByRole("switch", { name: "Make this epic private" }).
getByRole("switch", { name }) resolves it.components/ui/Switch.tsx (the primitive — only aria-label is forwarded).app/(authed)/issues/[key]/_components/EpicPrivacyControl.tsx (passes aria-labelledby).tests/e2e/epic-privacy-flow.spec.ts (6.14.9 — currently selects the switch by role alone, with a comment pointing here).