Skip to content

Validation Message

Form InfrastructureSince 0.2.0
import { ValidationMessage } from '@absoluteui/components/validation-message';
import '@absoluteui/components/validation-message/style.css';

Using Angular? See Validation Message for Angular.

A standalone field message for controls that AbsoluteUI does not render itself. ValidationMessage shows an error, warning, success, or info message with a tone icon, adds its id to the target’s aria-describedby, and, for errors, sets aria-invalid="true" on the target. AbsoluteUI inputs render the same aui-validation-message markup internally, so standalone and built-in messages look identical.

  • Messages for consumer-rendered or third-party controls that should look and behave like AbsoluteUI field messages.
  • Non-error field feedback such as an availability check result (success, warning, info).
  • AbsoluteUI inputs and Form Field: they render their own messages; use setCustomError() for external errors.
  • Form-level or page-level errors: use Error Summary.
  • Transient notifications: use a toast.

Error message for a consumer-rendered control

Section titled “Error message for a consumer-rendered control”
import { ValidationMessage } from '@absoluteui/components/validation-message';
import { TextInput } from '@absoluteui/components/text-input';
const inputHost = document.createElement('div');
const messageHost = document.createElement('div');
document.body.append(inputHost, messageHost);
const input = TextInput.create(inputHost, { label: 'Coupon code', name: 'coupon' }).input;
// Adds its id to the input's aria-describedby and sets aria-invalid="true" for tone 'error'.
const message = ValidationMessage.create(messageHost, { for: input, message: 'Enter a coupon code.' });
input.addEventListener('input', () => {
if (input.value !== '') message.clear();
});
import { ValidationMessage } from '@absoluteui/components/validation-message';
import { TextInput } from '@absoluteui/components/text-input';
const inputHost = document.createElement('div');
const messageHost = document.createElement('div');
document.body.append(inputHost, messageHost);
const input = TextInput.create(inputHost, { label: 'Handle', name: 'handle' }).input;
const message = ValidationMessage.create(messageHost, { for: input });
async function check(handle: string, signal: AbortSignal): Promise<void> {
message.setMessage('Checking availability…', 'info');
const response = await fetch(`/api/handles/${encodeURIComponent(handle)}`, { signal });
if (response.status === 404) message.setMessage('This handle is available.', 'success');
else if (response.ok) message.setMessage('This handle is taken.', 'error');
else message.setMessage('Availability could not be checked.', 'warning');
}
let pending: AbortController | null = null;
input.addEventListener('change', () => {
pending?.abort();
const controller = new AbortController();
pending = controller;
check(input.value, controller.signal).catch(() => {
// A newer check aborted this one and owns the message.
if (!controller.signal.aborted) message.clear();
});
});
import { ValidationMessage } from '@absoluteui/components/validation-message';
const messageHost = document.createElement('div');
document.body.append(messageHost);
ValidationMessage.create(messageHost, { message: 'Saved as draft.', tone: 'info', icons: { info: 'check' } });
import { ValidationMessage } from '@absoluteui/components/validation-message';
import '@absoluteui/components/validation-message/style.css';
const input = document.querySelector<HTMLInputElement>('#promo');
const host = document.querySelector<HTMLElement>('#promo-message');
if (input && host) {
const message = ValidationMessage.create(host, { for: input });
input.addEventListener('change', () => {
if (input.value === '') message.clear();
else if (/^[A-Z0-9]{6}$/.test(input.value)) message.setMessage('Code applied.', 'success');
else message.setMessage('Enter a 6-character code.', 'error');
});
}
import { ValidationMessage } from '@absoluteui/components/validation-message';
const input = document.createElement('input');
const messageHost = document.createElement('div');
document.body.append(input, messageHost);
ValidationMessage.create(messageHost, { for: input, message: 'Enter a coupon code.' });
<div class="aui-validation-message aui-validation-message--error" id=auto [hidden]> (consumer host)
<svg class="aui-validation-message__icon aui-icon" aria-hidden="true">
<span class="aui-validation-message__text">Message</span>
</div>
<input aria-describedby="... auto" aria-invalid="true"> (target, `for`)
ValidationMessage.create(host: HTMLElement, options?: ValidationMessageOptions): ValidationMessage

Global configuration key: validationMessage (see Configuration).

Option Type Default Description
message string "" Message text. Empty hides the message and unlinks the target.
tone 'error' | 'warning' | 'success' | 'info' "error" Visual tone and icon. Only ‘error’ sets aria-invalid on the target.
for HTMLElement | null Control described by the message: receives the host id in aria-describedby while a message is shown, and aria-invalid for errors.
icons Partial<{ error: IconRef; warning: IconRef; success: IconRef; info: IconRef }> Icon overrides per tone.
Property Type Description
host HTMLElement Consumer host; receives an id if it has none.
message string Current message (‘’ when hidden).
tone ValidationMessageTone Current tone.
Method Description
setMessage(message: string, tone?: ValidationMessageTone): void Set the message and optionally the tone.
clear(): void Hide the message and unlink the target.
update(options: Partial<ValidationMessageOptions>): void Merge and apply options; throws before changing anything on an invalid tone or target.
destroy(): void Unlink the target, remove rendering and classes, and unhide the host.

All rules live in the aui.components cascade layer, so application styles override them without specificity battles.

Class Purpose
aui-validation-message Host (also used by the built-in field error element).
aui-validation-message--error | --warning | --success | --info Tone modifiers.
aui-validation-message__icon Tone icon.
aui-validation-message__text Message text.
Slot Default icon
error alert-circle
warning alert-triangle
success check-circle
info info

Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.

--aui-color-error-fg | --aui-color-info-fg | --aui-color-success-fg | --aui-color-warning-fg | --aui-font-family-sans | --aui-font-size-sm | --aui-icon-size-sm | --aui-line-height-normal | --aui-space-1

type ValidationMessageTone = 'error' | 'warning' | 'success' | 'info';
interface ValidationMessageIcons { error: IconRef; warning: IconRef; success: IconRef; info: IconRef }
interface ValidationMessageOptions { message?: string; tone?: ValidationMessageTone; for?: HTMLElement | null; icons?: Partial<ValidationMessageIcons> }
interface ValidationMessageConfig {}

Pattern: Field description referenced by aria-describedby, with aria-invalid on the target for errors (HTML/ARIA).

Key Context Action
None Any Not focusable; the message is read with the target when the target is focused.
Element Role
host none (generic container)
icon none (aria-hidden=true)
Attribute Element Value
aria-describedby target Host id added while a message is shown; other ids are kept
aria-invalid target “true” while an error-tone message is shown; removed when the tone changes, the message clears, or the target changes, but only if this message set it. An aria-invalid=“true” the consumer set beforehand is never removed

Not focusable and never moves focus.

Criterion Level Note
1.3.1 Info and Relationships A
1.4.1 Use of Color A
1.4.3 Contrast (Minimum) AA
3.3.1 Error Identification A
3.3.3 Error Suggestion AA
4.1.2 Name, Role, Value A

Automated test results: Accessibility and compliance.

  • Tones pair color with a distinct icon; the message text must still state the problem.
  • Only the error tone marks the target invalid; warning, success, and info are descriptive only.
  • The message is not a live region; announce important changes separately if the user is not on the target.
  • Screen-reader reading of the description has not yet been manually verified with NVDA, JAWS, and VoiceOver.

hidden (empty message), error, warning, success, info.

From Trigger To
hidden message set visible; target described; aria-invalid for error tone
error tone changes to warning/success/info aria-invalid removed (if this message set it)
visible for changes old target unlinked; new target linked
visible clear() or empty message hidden; target unlinked
any destroy() target unlinked; host emptied and unhidden
  • Tones pair color with a distinct icon; the text must still state the problem (WCAG 1.4.1).
  • Only the error tone marks the target invalid; the other tones are descriptive only.
  • The message is not a live region: it is read when the user reaches the target. Announce important changes separately (for example with announce() from @absoluteui/utilities) if the user is elsewhere.
  • Manual screen-reader verification is pending (see Known limitations).

Icon and text in a row; long messages wrap anywhere.

Follows the document direction; the icon sits at the inline start.

Tone foreground role tokens only; no background. Forced-colors mode uses system text colors, with the icon distinguishing tones.

None. validationMessageConfig is registered with an empty schema, so configureAbsoluteUI({ components: { validationMessage: {} } }) accepts no options.

All options are instance options and dynamic through update().

icons.error (default alert-circle), icons.warning (alert-triangle), icons.success (check-circle), and icons.info (info).

The message uses textContent and is never parsed as HTML. Do not include raw user input in messages. CSP-safe.

No persistence, network access, or telemetry.

No listeners or subscriptions. Each update re-renders the icon and text.

One message per instance, one target per message.

  • An empty message hides the host and removes the reference from the target, keeping the other aria-describedby ids.
  • Without for, the message is only visual.
  • If the target already has aria-invalid="true" when an error is shown, the message leaves it to the consumer and does not remove it later.
  • destroy() keeps the generated host id.
Code Category Cause
VALIDATION_MESSAGE_OPTIONS configuration tone is not error, warning, success, or info; or for is not an HTMLElement or null.

A failed construction or update leaves no rendering or ARIA changes behind.

  • Not a live region.
  • The icon element is re-created on every update.
  • No locale: the text is consumer-provided.
  • Screen-reader reading has not yet been manually verified with NVDA, JAWS, and VoiceOver.
  • HTML or links in the message.
  • One message describing several targets.