Accessibility Guide
January 26, 2026 · View on GitHub
ngx-vest-forms is built with accessibility in mind, following WCAG 2.2 Level AA guidelines.
Quick Reference
Field-Level Messages (Polite)
The ngx-control-wrapper component uses polite announcements for inline field errors, warnings, and pending states:
Backward Compatibility: The legacy selector
sc-control-wrapperstill works in v2.x and provides the same accessibility features. See the Selector Prefix Migration Guide for migration details.
role="status"witharia-live="polite"- Avoids interrupting typing flow
- Prevents excessive screen reader chatter
Warnings may also appear after validationConfig-triggered validation, even if the
field was not touched yet. Use NGX_WARNING_DISPLAY_MODE_TOKEN or
warningDisplayMode="on-touch" to require touch-only warning visibility.
The wrapper also:
- merges its own
aria-describedbytokens with any consumer-provided tokens (so existing hint text associations are preserved) - toggles
aria-invalidonly when errors are meant to be shown (based on the configured error display mode)
Group containers (NgModelGroup) — avoid stamping descendant controls
When applying a wrapper to an NgModelGroup container (i.e. a region that contains multiple inputs), do not automatically set aria-describedby/aria-invalid on all descendant controls.
Use one of these patterns:
- Prefer per-field wrappers (each input has its own
<ngx-control-wrapper>) and keep group-level messages separate. - Prefer using the dedicated
<ngx-form-group-wrapper>forNgModelGroupcontainers (group-safe by default). - If you still want to use
<ngx-control-wrapper>around a group container, set:ariaAssociationMode="none"(group-safe mode)
Recommended pattern — ngModelGroup directly on the wrapper:
<!-- ngModelGroup directly on ngx-form-group-wrapper (recommended) -->
<ngx-form-group-wrapper ngModelGroup="address">
<ngx-control-wrapper>
<label for="street">Street</label>
<input id="street" name="street" [ngModel]="formValue().address?.street" />
</ngx-control-wrapper>
<ngx-control-wrapper>
<label for="city">City</label>
<input id="city" name="city" [ngModel]="formValue().address?.city" />
</ngx-control-wrapper>
</ngx-form-group-wrapper>
This keeps ARIA relationships predictable and avoids surprising changes across an entire group.
Unique id attributes (avoid duplicates)
Ensure form control id attributes are unique in the DOM.
If you render the same child component multiple times on a page (for example, billing + shipping address),
avoid hard-coded IDs like id="street". Prefer an ID prefix derived from the group/property path
(for example, billingAddress-street, shippingAddress-street).
See: Child Form Components.
Custom wrappers
If you build a custom wrapper component, you can use FormErrorControlDirective to get:
- stable region IDs (
errorId,warningId,pendingId) - the same
aria-describedbymerging behavior aria-invalidwiring
See: Custom Control Wrappers.
Form-Level Errors (Assertive)
For blocking post-submit errors, implement a form-level summary with assertive announcements:
<!-- Keep in DOM; update on submit -->
<div id="form-errors" role="alert" aria-live="assertive" aria-atomic="true">
<!-- Populate with error summary when form submission fails -->
</div>
Why This Strategy?
Polite inline messages:
- Update frequently as users type
- Non-critical (users can continue typing)
- Following WCAG guidance for continuously updating content
Assertive form-level errors:
- Block critical actions (form submission)
- Require immediate attention
- Following WCAG ARIA19/ARIA22 guidance for blocking errors
Implementation Details
See the ControlWrapperComponent docs for comprehensive accessibility features including:
- Automatic ARIA ID generation
aria-describedbyassociationsaria-invalidstate management- Unique region identification
Component docs:
For directive-only composition (no UI), see FormErrorControlDirective.
Validation & Testing
Always validate with:
- Screen readers: NVDA, JAWS, VoiceOver
- Automated tools: Accessibility Insights
- Real users: People who use assistive technologies
References
- WCAG ARIA19 - Using role=alert
- WCAG ARIA22 - Using role=status