Form Field
import { FormField } from '@absoluteui/components/form-field';import '@absoluteui/components/form-field/style.css';Using Angular? See Form Field for Angular.
What it does
Section titled “What it does”Field chrome and validation for a consumer-owned native <input>, <textarea>, or <select>. FormField adds the label, required indicator, help text, and validation message, maps the browser’s ValidityState to AbsoluteUI’s localized messages, applies the shared message timing, and registers the field with Form. The control element stays yours: it is not cloned or re-created, and only its ARIA wiring and the surrounding chrome are managed.
When to use
Section titled “When to use”- Native controls AbsoluteUI does not render itself: type=number, text-like input types (text, email, url, tel, search, password), a native <select> or <select multiple>, or a <textarea> with native attributes.
- Progressive enhancement of server-rendered inputs that must keep their element and attributes.
- Making a plain native element take part in Form validation and the error summary.
When not to use
Section titled “When not to use”- Text entry with counters, addons, or password reveal: use Text Input or Textarea.
- Checkboxes, radios, and switches: use Checkbox, Radio Group, or Switch (Form Field rejects them).
- Date and time values: use Date Input, Date Picker, or Time Picker (date, time, datetime-local, month, and week inputs are rejected).
- Range sliders and color pickers: range and color inputs are rejected.
- File uploads: use File Upload.
Examples
Section titled “Examples”Label and validate a native input
Section titled “Label and validate a native input”import { FormField } from '@absoluteui/components/form-field';
const control = document.createElement('input');control.name = 'zip';control.inputMode = 'numeric';control.autocomplete = 'postal-code';control.pattern = '[0-9]{5}';const host = document.createElement('div');document.body.append(host);const zip = FormField.create(host, { label: 'ZIP code', control, required: true, description: 'Five digits' });host.addEventListener('aui-change', () => { host.dataset.valid = String(zip.validity.valid);});Native select and textarea with consumer validation
Section titled “Native select and textarea with consumer validation”import { FormField, type FormFieldChangeDetail } from '@absoluteui/components/form-field';
const topic = document.createElement('select');topic.name = 'topic';topic.add(new Option('Choose a topic', ''));topic.add(new Option('Billing', 'billing'));topic.add(new Option('Technical support', 'support'));const topicHost = document.createElement('div');document.body.append(topicHost);FormField.create(topicHost, { label: 'Topic', control: topic, required: true });
const details = document.createElement('textarea');details.name = 'details';details.maxLength = 500;const detailsHost = document.createElement('div');document.body.append(detailsHost);FormField.create(detailsHost, { label: 'Details', control: details, validate: (value) => (typeof value === 'string' && /https?:\/\//.test(value) ? 'Remove links from your message.' : null),});detailsHost.addEventListener('aui-input', (event) => { const { validity } = (event as CustomEvent<FormFieldChangeDetail>).detail; detailsHost.dataset.valid = String(validity.valid);});Enhance server-rendered markup and refresh after programmatic changes
Section titled “Enhance server-rendered markup and refresh after programmatic changes”import { FormField } from '@absoluteui/components/form-field';
const host = document.querySelector<HTMLElement>('#quantity-field');const control = host?.querySelector<HTMLInputElement>('input[type="number"]');if (host && control) { const quantity = FormField.create(host, { label: 'Quantity', control }); // Native attributes are the constraints; re-read them after changing them in code. control.max = '10'; control.value = '12'; quantity.refresh(); quantity.reportValidity();}Show messages on first input (Mode B)
Section titled “Show messages on first input (Mode B)”import { configureAbsoluteUI } from '@absoluteui/core';import '@absoluteui/components/form-field';
configureAbsoluteUI({ components: { formField: { validationTiming: 'input' } } });import { FormField } from '@absoluteui/components/form-field';import '@absoluteui/components/form-field/style.css';
const host = document.querySelector<HTMLElement>('#guests-field');const control = host?.querySelector<HTMLInputElement>('input[name="guests"]');if (host && control) { const guests = FormField.create(host, { label: 'Number of guests', control, required: true, description: 'Up to 8 people' }); host.addEventListener('aui-change', () => { if (!guests.validity.valid) guests.reportValidity(); });}More usage
Section titled “More usage”import { FormField } from '@absoluteui/components/form-field';
const control = document.createElement('input');control.name = 'zip';control.pattern = '[0-9]{5}';const host = document.createElement('div');document.body.append(host);FormField.create(host, { label: 'ZIP code', control, required: true, description: 'Five digits' });Anatomy
Section titled “Anatomy”<div class="aui-field aui-form-field" data-aui-control> (consumer host; children replaced) <label class="aui-field__label" for=control>Label<span class="aui-field__required" aria-hidden="true">*</span></label> <input|textarea|select class="aui-form-field__control"> (consumer's element, moved into the host) <div class="aui-field__description"> (optional) <div class="aui-field__error aui-validation-message"> (when shown)</div>API reference
Section titled “API reference”Factory
Section titled “Factory”FormField.create(host: HTMLElement, options: FormFieldOptions): FormFieldGlobal configuration key: formField (see Configuration).
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
label |
string |
Visible label and accessible name (<label for> the control). Required. | |
control |
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement |
The native control to wrap; moved into the host. It receives an id if it has none. Choice, file, hidden, button, date/time (date, time, datetime-local, month, week), range, and color input types are rejected. Required. Set at creation only. | |
hideLabel |
boolean |
false |
Visually hide the label; it remains the accessible name. |
description |
string |
Help text referenced by aria-describedby. | |
required |
boolean |
Sets the native required property and the indicator. When omitted, the control’s own required attribute is used. | |
validate |
(value: string | string[]) => string | null |
Consumer validation after native constraints; return a message to mark the value invalid. string[] for <select multiple>. | |
icons |
Partial<{ error: IconRef }> |
Icon slot overrides. | |
validationTiming |
'blur' | 'input' | 'submit' |
"blur" |
When validation messages first appear; afterwards they update live. Globally configurable. |
locale |
string |
"en-US" |
Message and number-formatting locale. Globally configurable. Inherited from common configuration. |
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
host |
HTMLElement |
Consumer host; its children are replaced by the chrome and the control. |
control |
FormFieldControl |
The wrapped native control. |
value |
string | string[] |
Current value; string[] for <select multiple>. |
name |
string | null |
The control’s name, or null when empty. |
label |
string |
Label text. |
disabled |
boolean |
The control’s disabled property. |
validity |
FieldValidity |
{ valid, error, message }; recomputed on each render. |
dirty |
boolean |
Value differs from the value at creation. |
touched |
boolean |
Focus has left the control at least once. |
Methods
Section titled “Methods”| Method | Description |
|---|---|
refresh(): void |
Re-read native constraints, setCustomValidity() state, and the value after changing them in code. |
update(options: Partial<Omit<FormFieldOptions, 'control'>>): void |
Merge and apply options. |
reportValidity(): boolean |
Show messages now (marks the field submitted) and return validity. |
setCustomError(message: string | null): void |
External error (server, cross-field rule); cleared when the user types. null or ‘’ clears it. |
getFormValue(): string | string[] |
Value for form serialization. |
focus(): void |
Focus the control. |
reset(): void |
Restore the value (or selected options) captured at creation and clear dirty/touched/submitted/custom-error state. |
destroy(): void |
Remove the chrome, listeners, registration, and subscription; restore the consumer’s aria-describedby. The control stays in the host. |
Events
Section titled “Events”| Event | Detail | Bubbles | Cancelable | Description |
|---|---|---|---|---|
aui-input |
{ value: string | string[]; validity: FieldValidity } |
Yes | No | Native input event on the control. |
aui-change |
{ value: string | string[]; validity: FieldValidity } |
Yes | No | Native change event on the control. |
Content areas
Section titled “Content areas”| Area | Description |
|---|---|
control |
The consumer’s native element, placed between the label and the help text. |
CSS classes
Section titled “CSS classes”All rules live in the aui.components cascade layer, so application styles override them without specificity battles.
| Class | Purpose |
|---|---|
aui-field aui-form-field |
Host. |
aui-form-field--invalid |
Host modifier while a message is shown. |
aui-field__label | __required | __description | __error |
Shared field chrome. |
aui-form-field__control |
Added to the native control: AbsoluteUI control surface. |
Icon slots
Section titled “Icon slots”| Slot | Default icon |
|---|---|
error |
alert-circle |
Design tokens
Section titled “Design tokens”Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.
--aui-border-width | --aui-color-border | --aui-color-border-strong | --aui-color-disabled-bg | --aui-color-error-fg | --aui-color-focus-ring | --aui-color-surface | --aui-color-surface-sunken | --aui-color-text | --aui-color-text-disabled | --aui-color-text-muted | --aui-control-height-md | --aui-control-padding-x | --aui-duration-fast | --aui-easing-standard | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-md | --aui-font-size-sm | --aui-font-weight-medium | --aui-icon-size-sm | --aui-line-height-normal | --aui-line-height-tight | --aui-radius-md | --aui-space-1 | --aui-space-2
TypeScript definitions
Section titled “TypeScript definitions”type FormFieldControl = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;type FormFieldValue = string | string[];interface FormFieldIcons { error: IconRef }interface FormFieldChangeDetail { value: FormFieldValue; validity: FieldValidity }interface FormFieldOptions { label: string; control: FormFieldControl; hideLabel?: boolean; description?: string; required?: boolean; validate?: (value: FormFieldValue) => string | null; validationTiming?: ValidationTiming; locale?: string; icons?: Partial<FormFieldIcons>;}Accessibility
Section titled “Accessibility”Pattern: Native form control with a <label for> association (HTML); the consumer’s element keeps its native role.
Keyboard
Section titled “Keyboard”| Key | Context | Action |
|---|---|---|
| Tab / Shift+Tab | Any | Moves focus to and from the control. |
| All native keys | Control focused | Native behavior of the element (editing, arrow stepping, select picker). |
| Element | Role |
|---|---|
| control | Native role of the wrapped element (textbox, spinbutton, combobox/listbox for <select>, …) |
| error icon | none (aria-hidden=true) |
ARIA attributes
Section titled “ARIA attributes”| Attribute | Element | Value |
|---|---|---|
aria-describedby |
control | Help text and visible error ids, followed by ids the consumer had set before creation |
aria-invalid |
control | “true” only while an error message is shown |
required |
control | Native attribute, set from the required option when given |
The control shows the shared control focus indicator (border in color-focus-ring plus a ring); forced-colors mode uses a Highlight outline.
WCAG 2.2 success criteria addressed
Section titled “WCAG 2.2 success criteria addressed”Automated test results: Accessibility and compliance.
Notes and known caveats
Section titled “Notes and known caveats”- The required asterisk is aria-hidden; the native required attribute conveys the state.
- Native validation bubbles are suppressed (the invalid event is prevented) so only the AbsoluteUI message is shown and read.
- Errors appear after the first blur by default; when focus left because a pointer was pressed elsewhere, the message renders after the pointer is released so the click is not displaced.
- Existing aria-describedby references (for example an external hint) are kept and restored on destroy.
- Behavior with every native input type has not been manually verified with NVDA, JAWS, and VoiceOver.
Behavior
Section titled “Behavior”States
Section titled “States”default, focus-visible, filled, disabled, read-only, required, invalid (message shown).
State transitions
Section titled “State transitions”| From | Trigger | To |
|---|---|---|
| pristine | user edits | dirty; message shown only with validationTiming: 'input' |
| any | control blurs | touched; messages shown (blur timing), deferred until pointer release when a pointer caused the blur |
| any | reportValidity() (for example on form submit) |
submitted; messages shown for every timing |
| invalid (custom error) | user types | custom error cleared, validity recomputed |
| any | reset() |
value at creation; pristine, untouched |
Accessibility notes
Section titled “Accessibility notes”- The required asterisk is
aria-hidden; the nativerequiredattribute conveys the state. - Native validation bubbles are suppressed (the
invalidevent is prevented), so only the AbsoluteUI message is shown and read. - Errors appear after the first blur by default. When the blur was caused by pressing a pointer elsewhere, the message renders after the pointer is released so a click on Submit is not displaced.
- Missing values on a
<select>use “Select an option.”; text-like controls use “This field is required.” - Manual screen-reader verification is pending (see Known limitations).
Responsive behavior
Section titled “Responsive behavior”The control fills its container (inline-size: 100%, min-inline-size: 0). Textareas resize vertically. Density tokens change the control height.
Right-to-left
Section titled “Right-to-left”Follows the document direction; the chrome uses logical properties.
Theming
Section titled “Theming”Role tokens through the shared control-surface and text-control-states mixins; a <select> keeps its native picker and only the closed control is styled. In forced-colors mode, focus draws a Highlight outline.
Global configuration
Section titled “Global configuration”configureAbsoluteUI({ components: { formField: { validationTiming: 'input' } } });Changes apply to existing instances immediately.
Component configuration
Section titled “Component configuration”Instance options override global and theme defaults. All options except control are dynamic.
icons.error (default alert-circle).
Security
Section titled “Security”All text uses textContent. Validation messages never include the entered value, only constraint parameters. The consumer’s element is not cloned, so its listeners and attributes survive. CSP-safe.
Data handling
Section titled “Data handling”No persistence, network access, or telemetry. The native control carries its own value for form submission.
Performance
Section titled “Performance”Four listeners on the control (input, change, blur, invalid) and one configuration subscription. Each event re-renders the chrome once. No dedicated performance budget.
Supported data scale
Section titled “Supported data scale”One control per field. Unit tests cover each supported element type; large forms are expected to scale linearly.
Edge cases
Section titled “Edge cases”- Native
minlength/maxlengthapply only after user edits in browsers; Form Field also enforces them for programmatic values (counted in code points). <select multiple>values arestring[];dirtycompares the selected values with those at creation.requiredomitted keeps the control’s own attribute;required: falseremoves it.destroy()leaves the generated id and therequiredvalue on the control.- Numeric
min,max, andstepare read from the attributes;step="any"disables step checking. The step base follows HTML:min, else thevalueattribute, else 0. - A native
setCustomValidity(message)takes precedence over every other check and yields errorcustomwith the nativevalidationMessage(the consumervalidatecallback does not run). CallsetCustomValidity('')and thenrefresh()to clear it.
Errors
Section titled “Errors”| Code | Category | Cause |
|---|---|---|
FORM_FIELD_OPTIONS |
configuration | Missing or blank label; control is not an input, textarea, or select; or the input type is checkbox, radio, file, hidden, button, submit, reset, image, date, time, datetime-local, month, week, range, or color (use Checkbox, Radio Group, Switch, Date Input, Date Picker, or Time Picker). |
Validation (validity.error) |
validation | value-missing, type-mismatch, too-short, too-long, pattern-mismatch, bad-input, range-underflow, range-overflow, step-mismatch, custom. |
A failed construction leaves the host and the control untouched.
Known limitations
Section titled “Known limitations”- After changing constraint attributes or the value in code, call
refresh(). - The initial value for
dirtyandreset()is captured at creation. - No character counter.
- Screen-reader behavior across native input types has not yet been manually verified with NVDA, JAWS, and VoiceOver.
Unsupported scenarios
Section titled “Unsupported scenarios”- checkbox, radio, file, hidden, button, submit, reset, image, date, time, datetime-local, month, week, range, and color inputs.
- More than one control per field.
- HTML in the label, help text, or messages.