Accessibility
@prepared911/ui-forms delegates per-control semantics to @prepared911/ui-core primitives while owning the react-hook-form wiring: labels, aria-invalid, helper and error text ids, and submit/disable contracts on form shells. Your feature owns schema messages, button copy, async error text, and focus behavior after submit or dismiss.
Form* wrappers pass id, label, and htmlFor through ui-core inputs so every control has a visible or programmatic name.aria-invalid and link helper/error text via aria-describedby where the underlying primitive supports it.Form defaults noValidate so browser tooltips do not compete with Zod messages.<button type="submit"> inside the native <form> (or modal confirm wired to handleSubmit).StandardForm and DrawerForm disable Save when pristine or submitting, preventing silent no-op submits.ModalForm and DrawerForm use ui-core modal and ui-drawer focus traps while open.message keys in Zod schemas; plain English-only strings fail i18n review.setError + focus first invalid field).FormActionIntent.Destructive submits.SettingsRow, useDebouncedFieldSubmit) when save outcome matters mid-task.ModalForm, Enter on the primary field should not double-fire confirm unless intentional.ModalForm (noDismiss). On DrawerForm, Escape/close while dirty routes through the unsaved-change flow.<form> per shell—don't nest forms.FormSection accordion triggers are real buttons with expanded/collapsed state. Section titles must be readable without relying on color-only status dots; pair dots with text or aria-label on the trigger when status is critical.FormCombobox and FormSelect inherit listbox/combobox roles from ui-core; keep option labels concise.FormFileUpload announces file count changes; provide clear empty-state copy.form.setFocus on the first key in form.formState.errors inside onInvalid.ModalForm closes via handleClose; return focus to the element that opened the modal.DrawerForm shows UnsavedChangesModal, focus stays trapped until the operator confirms discard or returns to edit.FormHelperText or row-level helper.role="status" summary ("3 fields need attention") when SectionedForm panels are collapsed.FormSection error dots reflect field errors; ensure that the accordion header text names the section so the dot is not the only cue.SettingsRow debounced save should expose saving/error state in visible helper text; optional polite live region for "Saved" when the row is easy to miss visually.UnsavedChangesModal exposes a heading and Keep editing / Discard actions (no body copy). Ensure button names stay clear.Form shells use minimal motion (e.g., accordion expand or drawer slide). Respect prefers-reduced-motion: reduce in any custom field animations; ui-core primitives gate transitions where applicable.
Footer Save/Cancel buttons use default ui-core button sizes (44×44 minimum on touch layouts). Checkbox and switch rows should not shrink hit targets below threshold when labels wrap—preserve vertical padding on SettingsRow.
useZodForm validates on blur by default—screen reader users may not hear errors until leaving a field; critical fields can use mode: "onChange" selectively.FormSecret does not wire error UI—add explicit helper text if used for required secrets.ModalForm confirm is a button calling handleSubmit, not a native submit inside the modal footer—test keyboard activation explicitly.register fields with Form* fields in one form breaks typing and error paths—migrate atomically per field group.