Form inputs in AddContact and RecipientStep lack associated label elements
In src/components/ContactBook/AddContact.tsx, the name and address <input> elements only have a placeholder attribute, with no <label htmlFor>, aria-label, or aria-labelledby. The same pattern appears in src/components/SendFlow/RecipientStep.tsx's <textarea>.
Placeholder text disappears on input and isn't reliably exposed to screen readers as a field name, so these forms fail basic WCAG labeling requirements. Add visually-hidden <label>s (the codebase already has VisuallyHidden.tsx for exactly this purpose).
Additional Notes
Important scope correction: AddContact.tsx, RecipientStep.tsx, and the rest of src/components/SendFlow/* and src/components/ContactBook/* appear to be an unrouted, dead parallel implementation — the app's actual routed send flow is src/components/send/SendForm.tsx (not SendFlow/RecipientStep.tsx), and the live contact-management surface (if any) is separate from ContactBook/AddContact.tsx. Before investing in accessibility fixes here, confirm with whoever triages the new "dead code" issue filed in this batch whether this tree is being kept, deleted, or promoted to live — fixing labels on code that's about to be deleted would be wasted effort, but if the plan is to promote SendFlow/ContactBook to replace the current send/contact UI, this fix becomes directly load-bearing for real users.
Implementation sketch (assuming the components stay): use the existing VisuallyHidden component to wrap a <label htmlFor="contact-name">Contact name</label> / <label htmlFor="contact-address">Stellar address</label> pair above each respective input in AddContact.tsx, matching the ids to id props already on (or newly added to) the inputs; same pattern for the <textarea> in RecipientStep.tsx. This should be done consistently with the FormField component's own label-association bug (see the new issue on FormField not wiring htmlFor) — ideally both fixes land together so there's one consistent labeling pattern across the codebase rather than two different approaches (manual VisuallyHidden labels here vs. FormField's internal wiring elsewhere).
Edge cases: the address <textarea>/<input> likely also needs aria-describedby pointing at any inline validation error text (the weak-validation issue in #3 above) so a screen-reader user hears the error immediately, not just a generic "invalid" state; keep placeholder text as supplementary hint text, not a replacement for the label, per WCAG 1.3.1/4.1.2.
Testing strategy: @testing-library/react's getByLabelText('Contact name')/getByLabelText('Stellar address') queries currently fail against this component and would newly pass after the fix — a good, cheap regression test; run axe-core/jest-axe against rendered AddContact and RecipientStep output and assert zero label-related violations.
Cross-references: see the new issue about FormField never wiring htmlFor/aria-describedby — that's the same underlying labeling gap in the codebase's other, live forms (CreateRequestForm, EscrowForm, SubscriptionForm); and the new "dead code" issue, which should be resolved first to avoid wasted labeling work on code slated for deletion.
Form inputs in AddContact and RecipientStep lack associated label elements
In
src/components/ContactBook/AddContact.tsx, the name and address<input>elements only have aplaceholderattribute, with no<label htmlFor>,aria-label, oraria-labelledby. The same pattern appears insrc/components/SendFlow/RecipientStep.tsx's<textarea>.Placeholder text disappears on input and isn't reliably exposed to screen readers as a field name, so these forms fail basic WCAG labeling requirements. Add visually-hidden
<label>s (the codebase already hasVisuallyHidden.tsxfor exactly this purpose).Additional Notes
Important scope correction:
AddContact.tsx,RecipientStep.tsx, and the rest ofsrc/components/SendFlow/*andsrc/components/ContactBook/*appear to be an unrouted, dead parallel implementation — the app's actual routed send flow issrc/components/send/SendForm.tsx(notSendFlow/RecipientStep.tsx), and the live contact-management surface (if any) is separate fromContactBook/AddContact.tsx. Before investing in accessibility fixes here, confirm with whoever triages the new "dead code" issue filed in this batch whether this tree is being kept, deleted, or promoted to live — fixing labels on code that's about to be deleted would be wasted effort, but if the plan is to promoteSendFlow/ContactBookto replace the currentsend/contact UI, this fix becomes directly load-bearing for real users.Implementation sketch (assuming the components stay): use the existing
VisuallyHiddencomponent to wrap a<label htmlFor="contact-name">Contact name</label>/<label htmlFor="contact-address">Stellar address</label>pair above each respective input inAddContact.tsx, matching theids toidprops already on (or newly added to) the inputs; same pattern for the<textarea>inRecipientStep.tsx. This should be done consistently with theFormFieldcomponent's own label-association bug (see the new issue onFormFieldnot wiringhtmlFor) — ideally both fixes land together so there's one consistent labeling pattern across the codebase rather than two different approaches (manualVisuallyHiddenlabels here vs.FormField's internal wiring elsewhere).Edge cases: the address
<textarea>/<input>likely also needsaria-describedbypointing at any inline validation error text (the weak-validation issue in #3 above) so a screen-reader user hears the error immediately, not just a generic "invalid" state; keepplaceholdertext as supplementary hint text, not a replacement for the label, per WCAG 1.3.1/4.1.2.Testing strategy:
@testing-library/react'sgetByLabelText('Contact name')/getByLabelText('Stellar address')queries currently fail against this component and would newly pass after the fix — a good, cheap regression test; runaxe-core/jest-axeagainst renderedAddContactandRecipientStepoutput and assert zero label-related violations.Cross-references: see the new issue about
FormFieldnever wiringhtmlFor/aria-describedby— that's the same underlying labeling gap in the codebase's other, live forms (CreateRequestForm,EscrowForm,SubscriptionForm); and the new "dead code" issue, which should be resolved first to avoid wasted labeling work on code slated for deletion.