17Field
The wrapper that makes a control a form field: label above, help or error below, all wired with aria-describedby. Every control on a real form sits inside one.
Use it for
- Around every Input/Select/Textarea in a form
- Error display — the message replaces the help text and is announced via role="alert"
Not for
- Checkbox/radio/switch — their label wraps the control instead (see those pages)
Label + help
We send lesson updates here.
markup
<div class="ns-field" style="max-inline-size:20rem">
<label class="ns-field__label" for="f-email">Email address<span class="ns-field__required" aria-hidden="true">*</span></label>
<input class="ns-input" id="f-email" type="email" aria-describedby="f-email-h" required>
<p class="ns-field__help" id="f-email-h">We send lesson updates here.</p>
</div>Error state
Name what is wrong and what to do — never just "invalid".
That ID is 18 characters. This one has 9.
markup
<div class="ns-field" style="max-inline-size:20rem">
<label class="ns-field__label" for="f-org">Org ID</label>
<input class="ns-input ns-input--mono" id="f-org" value="00D5g0000" aria-invalid="true" aria-describedby="f-org-e">
<p class="ns-field__error" id="f-org-e" role="alert">That ID is 18 characters. This one has 9.</p>
</div>Accessibility contract
- The label is a real
- aria-invalid drives the error styling, so seen and announced state cannot drift
- The error is announced immediately (role="alert") because the user has already left the field when validation lands