Search code examples
htmllabelaccessibilityscreen-readers

a11y: Input fields, wrapped in a div, attached "by id" to a label


I have a time picker component, composed by two separate input fields that work together to create the illusion of being one.

This is how the skeleton looks like

<div> {/* component container */}

    <div> {/* container for hours:minutes */}
        <input /> {/* hours */}
        <input /> {/* minutes */}
    </div>

    <Select /> {/* custom React component, for AM/PM */}
</div>

Everything works great for "regular" users, but the accessibility is poor.

Because of pre-existing limitation in the codebase, it's hard for me to simply wrap everything in a label, which would solve my problem.


What I'm thinking of doing is the following, and I would like the community input (ha!) on whether this can be really useful to a screen reader or not.

<label htmlFor="myUniqueId">Here goes my label</label>
<div id="myUniqueId"> {/* component container */}

    <div> {/* container for hours:minutes */}
        <input /> {/* hours */}
        <input /> {/* minutes */}
    </div>

    <Select /> {/* custom React component, for AM/PM */}
</div>

So basically, set an ID on the container, instead of the inputs, and then attach a label using htmlFor.

Would this work for screen readers?


Solution

  • No, the for attribute must point at a labelable element:

    The for attribute may be specified to indicate a form control with which the caption is to be associated. If the attribute is specified, the attribute’s value must be the ID of a labelable element in the same Document as the label element.

    labelable elements are: input, button, select, textarea, fieldset, output, object, meter, progress, label, img.

    I would structure it this way:

    <label htmlFor="myUniqueId">Select time</label>
    <fieldset id="myUniqueId"> {/* component container */}
    
        <div> {/* container for hours:minutes */}
            <input aria-label="hours" /> {/* hours */}
            <input aria-label="minutes" /> {/* minutes */}
        </div>
    
        <Select aria-label="AM or PM" /> {/* custom React component, for AM/PM */}
    </fieldset>