Accessibility
@telixon/web-sdk writes exactly two things to the element: the formatted value and the caret
range. It never sets ARIA attributes, ids, classes, or the placeholder attribute. The
accessible name, descriptions, and error announcements are therefore your markup, and nothing
the adapter does will overwrite them. This page shows the wiring; it assumes a PhoneInput
attached as in getting started.
Markup baseline
Section titled “Markup baseline”<label for="phone">Phone number</label><input id="phone" type="tel" autocomplete="tel" aria-describedby="phone-hint phone-error" /><p id="phone-hint">Include the country calling code.</p><p id="phone-error" aria-live="polite"></p>- The
labelwithforgives the input its accessible name. A placeholder is not a name: it disappears as soon as the user types. createPhoneInputacceptstype="text"andtype="tel". Prefertype="tel": virtual keyboards show the telephone key layout. If the type must staytext, addinputmode="tel"for the same layout.autocomplete="tel"identifies the field to browser autofill as a full number including the calling code, which matches international mode. Useautocomplete="tel-national"when the value excludes the calling code: national mode, or international mode withdisplay: { callingCodeInInput: false }.
aria-invalid from validationError
Section titled “aria-invalid from validationError”state.validationError is recomputed on every state change, including mid-number: an empty
input reports { kind: 'EMPTY' } and a partial one reports TOO_SHORT. Reflecting it into
aria-invalid on every keystroke would mark the field invalid while the user is still typing,
so gate it on a commit point such as blur:
// inputElement and errorElement are the elements from the markup above.let touched = false;
function render(): void { const error = phoneInput.getState().validationError; const showError = touched && error !== null;
inputElement.setAttribute('aria-invalid', showError ? 'true' : 'false'); errorElement.textContent = showError ? messageFor(error) : '';}
inputElement.addEventListener('blur', () => { touched = true; render();});
phoneInput.subscribe(render);After the first blur the subscriber keeps the attribute and the message in sync on every keystroke, so the error clears the moment the number becomes valid.
Whether EMPTY is an error at all is your form’s required rule: for an optional field, treat it
as no error. Validation feedback covers the timing model
in depth, and form errors maps every ValidationError kind to
user-facing copy. A minimal messageFor:
import type { ValidationError } from '@telixon/core';
function messageFor(error: ValidationError): string { switch (error.kind) { case 'EMPTY': return 'Enter a phone number.'; case 'TOO_SHORT': return `Enter at least ${error.minLength} digits.`; default: return 'Enter a valid phone number.'; }}Live error region
Section titled “Live error region”Keep the error element in the DOM at all times and empty it when there is no error: live regions
announce changes only when the region exists before the change. aria-live="polite" waits for a
pause in speech; role="alert" interrupts, which is rarely right for a field the user is still
editing. Updating textContent, as render does above, triggers the announcement.
aria-describedby
Section titled “aria-describedby”List the hint id before the error id: descriptions are announced in the order the ids appear in the attribute. An empty error element contributes nothing, so the attribute can stay static; no ids need to be added or removed as the error appears and disappears.
Hints and placeholders
Section titled “Hints and placeholders”state.placeholder carries an example number for the resolved region and is exposed as data
only, never written to the element. Rendering it into the placeholder attribute works as an
example value, but keep the label and the hint: placeholder text vanishes on the first typed
digit. Placeholders covers resolution and display shaping.
Keyboard editing
Section titled “Keyboard editing”The adapter drives the input through native editing events, so keyboard editing keeps its platform behavior: selection replacement, word and line deletion, and the undo and redo gestures all run through the controller. There is nothing to re-implement; programmatic control lists the handled gestures.
Region picker semantics
Section titled “Region picker semantics”createRegionList emits pure state and renders nothing, so when you build a picker from it, the
combobox or listbox semantics, focus management, and keyboard interaction are yours to
implement. Region picker builds the markup and
input and picker sync wires it to the input.