Search topics
Input masks that change a typed or pasted number
A phone field's mask has ten fixed places, so a number pasted with +1 in front becomes another number, and the mask's blanks are read out as the field's value.
Inputmask('(999) 999-9999').mask(tel); The problem
People who paste a number, let the browser fill it in or type it their own way get a different number from the one they gave, and screen reader users hear a pattern of blanks as the field's value.
A shop's checkout asks for a phone number, so the courier can text when a parcel is on its way. The field is drawn with the format inside it, (__) -, so people can see what to type, and as they type, the digits fill the blanks. The developer builds it with one line of a popular mask library, or with a few lines of their own that add the brackets and the dash as each digit goes in.
A customer copies their number from their contacts, where it is saved as +1 212 555 0147, and pastes it. The field shows (121) 255-5014. It looks like a phone number, so they press Continue, and the texts go to someone else. The mask has ten places, and it fills them with the first ten digits it is given: the country code 1 becomes the start of the area code, and the last 7 is dropped. Typing +1 first does the same: the + is thrown away without a word, and the 1 takes the first place. We tried the mask in Chromium with Inputmask 5.0, IMask 7.6, Maska 3.2 and the hand-written version below, in October 2026, and each turned the pasted number into (121) 255-5014. IMask's tracker has the same kind of report for its +7 mask: a number pasted with a leading 8 comes out wrong.
The two ways it gets built fail in two more ways:
- With the library, the pattern is the field's value as soon as it takes focus. A screen reader reads the brackets and blanks as what the field holds; an Inputmask user reported it read out as "underline underline underline…".
- With the hand-written version, every change rebuilds the value, and the caret jumps to the end. Someone who notices a wrong fourth digit, deletes it and types the right one finds it added last: (212) 550-1479.
No success criterion fails here, and this guide is a best practice. Success Criterion 3.3.2 Labels or Instructions asks for labels or instructions where content needs input, and its Understanding document says they may give the expected format (technique G89, the format with an example). Both Befores pass it: the field has a label, and the library's pattern even shows the format. Nothing in WCAG asks a field to take a number in more than one form.
The advice comes from design systems and testing. GOV.UK's telephone number pattern says to avoid input masking, because it makes it harder for people to type a number the way they prefer and to copy one out from somewhere else and check it, and to allow spaces, hyphens, dashes and brackets, and country and area codes. Filament Group found the blanks were "little more than aural garbage" read aloud on focus, and that a script rewriting the value as people type makes screen readers read out fragments of it.
The failure is in how a mask reads what it is given. It knows one format with a fixed number of places, reads each character in turn, drops any its format doesn't take, such as the + and the spaces, and puts each digit in the next place until the places run out. A number written another way doesn't fit, and nothing tells the person what was dropped. The caret is the browser's doing: when a script sets an input's value, HTML moves the caret to the end of the text, so a handler that rebuilds the value on every change takes the caret away from where it was.
One part comes close to a criterion. Success Criterion 3.3.1 Error Identification asks that an input error the page detects is described in text, and its Understanding document says that when a value is too high or too low and the page's code changes it by itself to fall within the allowed range, the error still has to be described. A mask that throws away the + and a digit without a word is near that case. The fixes below throw nothing away, so the question doesn't come up.
Anyone who doesn't type the number digit by digit in the mask's own form meets this: people who paste it, people whose browser fills it in, and people who write it as it is printed on their card. A number saved in a contacts app, or filled in by the browser, can come with its country code in front, so the paste is not an odd case.
4 of 10 groups affected
- No vision (affected)
- Low vision (affected)
- Colour vision (not affected)
- No hearing (not affected)
- Hard of hearing (not affected)
- No speech (not affected)
- Motor (affected)
- Reach, strength (not affected)
- Cognitive (affected)
- Seizures (not affected)
- Without vision While the field has focus and nothing is typed, its value is the pattern (__) -, so a screen reader reads brackets and blanks as what the field holds. As digits go in, it reads a mix of digits and blanks back, and a pasted number that changed into another one is never announced.
- Limited manipulation People who type with a switch, a mouth stick or one finger copy a number, or let the browser fill it in, to save presses. The mask turns a number with +1 in front into a different one, and correcting it in a field that sends the caret to the end costs many more presses.
- Limited language, cognitive and learning abilities Checking a number against the one it was copied from is hard when the field shows it in another shape, and harder still when the digits have changed: (121) 255-5014 still looks like a phone number, so nothing says it is wrong.
- Limited vision Under magnification only part of the field may be in view. When the caret jumps to the end after a correction, the next digit goes in at the end, away from the digit they were looking at.
The field has a label, the page passes axe, and a scan never pastes anything. EaseWeb's audit pastes +1 212 555 0147 into each phone field, and 4242 4242 4242 4242 into each card number field, by script, then puts the value back without sending the form. It lists a field whose digits come out different, and a field whose value is a pattern of blanks, for a person to try. These are leads, not findings, since no criterion fails. Where the caret goes, and what a screen reader reads as each digit goes in, need a person. A code assistant asked for "a phone field formatted as (999) 999-9999" can write either Before.
Try it
Copy the number from the contact card, paste it into Phone number, and press Continue.
Broken 1: Tab into the field first and read what the panel under the form says. Then paste the number: which digits went in?
Broken 2: paste the number, then clear the field, type 2125550147 yourself, click just after the first 5 and change it with Backspace and another digit. Where does the caret go?
Apply a fix. Paste the number, or type it any way you like, with spaces, dots or brackets, and press Continue.
Broken Can you fix this?
Screen reader says:
(Tab to the field)
The fix
Take the number as it is typed, and check its digits.
A mask library with the pattern in the field
<form> <label for="tel">Phone number</label> <input id="tel" name="tel" type="tel" autocomplete="tel"> <button type="submit">Continue</button></form>What is wrong3 points
- When the field takes focus, its value becomes the pattern (__) -, and a screen reader reads that as what it holds.
- The mask has ten places. Pasted as +1 212 555 0147, the number fills them with its first ten digits and shows (121) 255-5014; the last 7 is dropped.
- The + is thrown away, and nothing says the number changed.
No mask, and the digits checked when the form is sent
<form> <label for="tel">Phone number</label> <p id="tel-hint">Include the area code, such as (212) 555-0147.</p> <input id="tel" name="tel" type="tel" autocomplete="tel" aria-describedby="tel-hint tel-error"> <p id="tel-error"></p> <button type="submit">Continue</button></form>How it is fixed5 steps
- Remove the mask, so the field keeps the number as it is typed or pasted, with any spaces, brackets, dashes or dots.
- Put an example under the label, tied to the field with
aria-describedby. It is read with the field, and stays on screen while people type. - When the form is sent, keep only the digits, and drop a leading 1 when there are eleven, so +1 212 555 0147, (212) 555-0147 and 212.555.0147 are the same ten digits.
- Give a value with the wrong number of digits a message that says what to enter, tied to the field.
- Send the ten digits, and keep them the same way on the server, since a request can reach it without this script.
This fixes both Befores. Nothing changes the field while people type, so the caret stays where they put it and a pasted number goes in whole; it passes 3.3.2, as the Befores did, with the format now in words.
Why this fix: Convert what people type instead of rejecting it
Both take a number however it is typed or pasted. Taking it with no mask is the one to use: it is the same check with less code, and it shows the number as the person wrote it, where GOV.UK's Notify team saw some people confused by a reformatted version of their number. Grouping once the field is left is for a design that wants every number shown the same way, such as a card number in fours; it changes the field once, after people have finished typing, and never moves the caret.
If a mask library has to stay, turning its pattern off takes the blanks out of the value (in Inputmask, showMaskOnFocus: false and showMaskOnHover: false; in IMask, the default lazy: true), but it is not a fix. In our tests, IMask with no pattern shown still turned the pasted +1 212 555 0147 into (121) 255-5014.
Leave maxlength off. One sized to the format, such as 14 for (212) 555-0147, cuts a pasted +1 212 555 0147 short in the same way, and GOV.UK's text input guidance warns that a strict length stops people formatting a value their usual way. Check the length of the digits when the form is sent instead.
Keep type="tel" and autocomplete="tel". The first brings up a phone keypad on touch screens and takes any character; the second lets the browser fill the number in, which may come with the country code, as the check above expects. Don't use type="number", which is for quantities and doesn't take spaces or brackets.
Card numbers follow the same rule: take the number with or without spaces, autocomplete="cc-number" and inputmode="numeric", and group it in fours once the field is left, if the design wants the number as it is printed on the card. See convert what people type instead of rejecting it. A date mask such as MM/DD/YYYY is the same failure; how to take a typed date is in date pickers that can't be typed in or used by keyboard.
A shop that takes numbers from more than one country has to keep the + and the country code, and needs a library that knows every country's numbering, such as libphonenumber, run when the form is sent rather than on each key.
Verify the fix
6 checks, no mouse.
Test with: Keyboard, Touch, Autofill, Screen reader
Record the OS, browser, assistive-technology versions, the build, the date, and the actual result of each step. Repeat after the shared phone field or its formatting changes.
Limits
This is a starting pattern, not a guarantee for every application. The example is a US number with ten digits and an optional country code 1; other countries need other lengths, and an international form keeps the +. A number asked for in three boxes, one for each group, is a different failure, covered in split date and phone fields with no name for each box. How a message reaches people after a failed submission is covered in form errors that don't say how to fix them.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
References
- WCAG Understanding: labels-or-instructions (opens in a new tab)
- WCAG Technique G89 (opens in a new tab)
- WCAG Understanding: error-identification (opens in a new tab)
- design-system.service.gov.uk: telephone-numbers (opens in a new tab)
- filamentgroup.com: politespace (opens in a new tab)
- github.com: 981 (opens in a new tab)
- github.com: 800 (opens in a new tab)
- github.com: 193 (opens in a new tab)
EaseWeb service
Fixed it? Keep it from coming back.
I turn guides like this one into rules your coding agents follow and checks your CI runs. Then I retest by hand with a keyboard and screen readers, periodically.