One place that makes a form control announceable
Field owns the scaffolding every labelled form control shares: the label and its htmlFor, the required marker, the helper or error message, the ids that tie them together, and the aria-describedby and aria-invalid wiring. Controls compose inside it and read that wiring through useField(), so accessibility is correct systemically rather than one component at a time. Input, Textarea, DateInput, Dropdown, Combobox and FileInput all build on it.
Default
We will only use this to contact you.
The label is associated with the control through a generated id, and the helper text is linked by aria-describedby so a screen reader announces it as part of the field.
Required
As it appears on your ID.
The asterisk is marked aria-hidden — it is a visual cue only. The control carries the real required attribute, which is what assistive technology announces.
Error
That address is not valid.
Error recolours the helper text to the error border colour and marks the control invalid. Because the message is already the described-by target, the reason for the error is announced with the field rather than being visual-only.
Disabled
Assigned automatically.
Disabled dims the label to the disabled input text colour. The control is disabled independently — Field styles the scaffolding, it does not disable its children for you.
Compact
Matches titles and descriptions.
Compact drops the label to the small paragraph size. Pass the same size to the control so the two stay in step.
Composing your own control
A custom control reads the wiring instead of deriving it. useField() returns null outside a Field, so the same control still renders standalone.
const field = useField();
<input
id={field?.controlId}
aria-describedby={field?.describedBy}
aria-invalid={field?.invalid}
required={field?.required}
disabled={field?.disabled}
/>