Search code examples
htmlaccessibilitywai-aria

Splitting HTML text input into multiple inputs while maintaining accessibility


I have a plain text input like the following on my page:

<label for="tfa_code">Verification Code</label>
<input type="text" name="tfa_code" id="tfa_code"/>
<button type="submit">Submit</button>

This is straightforward and needs no special accommodation for screen readers. But, there is front-end scripting that transforms it into a more typical-looking two-factor authentication entry, where each digit is entered into a single text input. Something like this:

<label for="tfa_code">Verification Code</label>
<input type="hidden" name="tfa_code" id="tfa_code"/>
<input type="text" id="regcode0" maxlength="1"/>
<input type="text" id="regcode1" maxlength="1"/>
<input type="text" id="regcode2" maxlength="1"/>
<input type="text" id="regcode3" maxlength="1"/>
<input type="text" id="regcode4" maxlength="1"/>
<input type="text" id="regcode5" maxlength="1"/>
<button type="submit">Submit</button>

Where there are keyup listeners on the inputs to move the cursor between fields and update the hidden input's value.

My concern is how to label this for the benefit of users with screen readers. My first thought was to simply wrap the inputs in a <fieldset> with a <legend> and remove the <label> element altogether. Will this sufficiently identify the inputs as part of a single logical group, properly describe their purpose to these users, and not spam them with voice prompts?

<fieldset>
    <legend>Verification Code</legend>
    <input type="hidden" name="tfa_code" id="tfa_code"/>
    <input type="text" id="regcode0" maxlength="1"/>
    <input type="text" id="regcode1" maxlength="1"/>
    <input type="text" id="regcode2" maxlength="1"/>
    <input type="text" id="regcode3" maxlength="1"/>
    <input type="text" id="regcode4" maxlength="1"/>
    <input type="text" id="regcode5" maxlength="1"/>
</fieldset>
<button type="submit">Submit</button>

I looked at attributes like aria-labelledby but I don't think it would be appropriate as (I think) it would cause the label to be read out each time an input got focus. And something like role="group" doesn't seem to provide anything beyond what the <fieldset> element does.


Solution

  • Per Andy's comment on the question, I decided not to split the field up for screen reader users at all, thereby avoiding the problem. The single field is hidden from standard browsers, and the split fields are hidden from screen readers:

    <label for="tfa_code">Verification Code</label>
    <input type="text" name="tfa_code" id="tfa_code" class="visually-hidden" autocomplete="one-time-code"/>
    <div aria-hidden="true">
        <input type="text" id="regcode0" maxlength="1"/>
        <input type="text" id="regcode1" maxlength="1"/>
        <input type="text" id="regcode2" maxlength="1"/>
        <input type="text" id="regcode3" maxlength="1"/>
        <input type="text" id="regcode4" maxlength="1"/>
        <input type="text" id="regcode5" maxlength="1"/>
    </div>
    <button type="submit">Submit</button>
    

    Testing is planned to ensure the visually hidden input is not trying to steal focus on standard browsers because of the autocomplete attribute, but it seems good so far.