What is wrong
components/ui/Input.tsx accepts an error prop and renders it, but nothing connects the message to the field:
{error && <p className="mt-1 text-xs text-accent">{error}</p>}
The <input> gets a red border and that is all. There is no aria-invalid, and no aria-describedby pointing at the error text. A screen reader user tabbing into a failed field hears the label and nothing else, and the error paragraph is only reachable by arrowing past the input in browse mode. The colour-only border is also the sole visual signal, which is exactly the pattern WCAG 1.4.1 rules out.
The same applies to the prefix and suffix spans: they render meaningful text such as a currency or unit marker but are not part of the field's accessible name or description.
What to change
In components/ui/Input.tsx:
- Generate a stable id for the component. React 18 is already a dependency, so
const reactId = useId(); is the right tool. Respect a caller-supplied props.id when there is one, and fall back to the generated value.
- Derive
const errorId = ${id}-error; and put it on the error <p>.
- On the
<input>, add aria-invalid={error ? true : undefined} and aria-describedby set to errorId when there is an error. Merge rather than clobber any aria-describedby the caller passed in.
- Give the error paragraph
role="alert" so it is announced when it appears after a submit attempt.
- Mark the
prefix and suffix spans aria-hidden="true" if they are decorative, or fold them into the description if they carry meaning. Pick one and say which in the PR.
Input is a forwardRef component, so keep the existing signature and ref forwarding intact.
Where it shows up
components/post/SubmitForm.tsx and components/post/EditCaseForm.tsx are the main consumers, so this fixes the accessibility of the submission flow, which is the single most important form on the site. No visual change should result.
Verify
npx tsc --noEmit
npm run lint
npm run format
Then trigger a validation error on /submit and confirm with a screen reader, or with the browser accessibility inspector, that the input is marked invalid and describes the error text.
Comment here to claim it and ask anything you are unsure about. You will usually get a reply within a day.
What is wrong
components/ui/Input.tsxaccepts anerrorprop and renders it, but nothing connects the message to the field:The
<input>gets a red border and that is all. There is noaria-invalid, and noaria-describedbypointing at the error text. A screen reader user tabbing into a failed field hears the label and nothing else, and the error paragraph is only reachable by arrowing past the input in browse mode. The colour-only border is also the sole visual signal, which is exactly the pattern WCAG 1.4.1 rules out.The same applies to the
prefixandsuffixspans: they render meaningful text such as a currency or unit marker but are not part of the field's accessible name or description.What to change
In
components/ui/Input.tsx:const reactId = useId();is the right tool. Respect a caller-suppliedprops.idwhen there is one, and fall back to the generated value.const errorId =${id}-error;and put it on the error<p>.<input>, addaria-invalid={error ? true : undefined}andaria-describedbyset toerrorIdwhen there is an error. Merge rather than clobber anyaria-describedbythe caller passed in.role="alert"so it is announced when it appears after a submit attempt.prefixandsuffixspansaria-hidden="true"if they are decorative, or fold them into the description if they carry meaning. Pick one and say which in the PR.Inputis aforwardRefcomponent, so keep the existing signature and ref forwarding intact.Where it shows up
components/post/SubmitForm.tsxandcomponents/post/EditCaseForm.tsxare the main consumers, so this fixes the accessibility of the submission flow, which is the single most important form on the site. No visual change should result.Verify
Then trigger a validation error on
/submitand confirm with a screen reader, or with the browser accessibility inspector, that the input is marked invalid and describes the error text.Comment here to claim it and ask anything you are unsure about. You will usually get a reply within a day.