A generic "Validation failed" toast on submit tells the shopper nothing about which of the twelve fields is wrong. They scroll, squint, guess, retry, and leave. Field-specific inline errors — surfaced on blur next to the offending input — turn a recovery into a three-second fix. Screen-reader users are hit hardest: a toast with no aria-live region and no role="alert" on the field error is invisible to them, excluding customers who rely on assistive tech.
Medium because generic errors slow recovery for everyone and effectively block screen-reader users from completing checkout.
Validate on onBlur per field and render a role="alert" span next to the input when validation fails. Write messages that name the rule ("Zip code must be 5 or 9 digits"), not the status ("Invalid"). Place this in src/components/AddressForm.tsx.
if (!/^\d{5}(-\d{4})?$/.test(zip)) return { zip: 'Zip code must be 5 or 9 digits (e.g., 90210)' }
{errors.zip && <span role="alert">{errors.zip}</span>}
ID: ecommerce-cart-ux.address-forms.validation-errors
Severity: medium
What to look for: Count all required address fields and enumerate the validation rules applied to each (e.g., regex patterns, required checks, length limits). For each field, classify the error message as: (a) field-specific and actionable (e.g., "Zip code must be 5 digits"), (b) generic (e.g., "Invalid input" or "Required"), (c) none. Check whether errors appear inline (next to the field) or only at the top of the form. Quote the error message strings found in the validation code.
Pass criteria: At least 3 address fields have field-specific validation with inline error messages that tell the user exactly what to fix. No more than 1 field should use a generic "Required" or "Invalid" message without elaboration. Report: "X of Y address fields have specific inline validation. Error messages reference the specific issue."
Fail criteria: Validation shows only generic errors (e.g., "Validation failed", "Invalid input"), or errors appear only after form submission at the top of the page with no per-field guidance.
Skip (N/A) when: No address form exists in the checkout flow (digital products only, no shipping).
Detail on fail: Example: "Address form in src/components/AddressForm.tsx has 5 required fields. 0 of 5 show inline errors. Single 'Validation failed' toast appears on submit. User cannot tell which field is wrong."
Cross-reference: For form accessibility requirements (error announcements for screen readers), the Accessibility Fundamentals audit covers form error handling. For server-side validation patterns, the API Security audit covers input validation.
Remediation: Add field-specific inline validation in your address form at src/components/AddressForm.tsx:
// src/components/AddressForm.tsx
function AddressForm() {
const [errors, setErrors] = useState({})
const validateField = (field, value) => {
if (field === 'zip' && !/^\d{5}(-\d{4})?$/.test(value)) {
return { zip: 'Zip code must be 5 or 9 digits (e.g., 90210)' }
}
if (field === 'city' && !value.trim()) {
return { city: 'City is required' }
}
return {}
}
return (
<div>
<input name="city" onBlur={(e) => setErrors(validateField('city', e.target.value))} />
{errors.city && <span className="error" role="alert">{errors.city}</span>}
<input name="zip" onBlur={(e) => setErrors(validateField('zip', e.target.value))} />
{errors.zip && <span className="error" role="alert">{errors.zip}</span>}
</div>
)
}