Mobile Accessibility Guidelines - Forms
Grouping form elements must
Controls, labels, and other form elements must be properly grouped.
Properly grouped form elements help all users understand the relationships between form controls and make forms easier to use. For users of assistive technology this can mean fewer steps and reduced complexity.
Correctly grouped controls also ensure standard behaviours work as expected.
There are four things to consider when grouping form elements:
- correctly associating labels with form controls,
- correctly associating related radio buttons or checkboxes,
- wrapping related form elements in a labelled container such as
fieldset
withlegend
, - keeping labels and legends succinct to minimise verbosity.
iOS
iOS does not provide a default mechanism for grouping related controls. However, there are a few steps that can be taken to assist VoiceOver users:
- Provide a visual label for the group above the set of controls, and code the label as a heading using
UIAccessibilityTraitHeader
. - Apply an
accessibilityLabel
to each of the individual related form controls that includes both the visual text, and the header text, so that a VoiceOver user will hear both concurrently.
In the following example, the visual homeAddressHeading
is 鈥淗ome Address鈥, while the deliveryAddressHeading
is 鈥淒elivery Address鈥. The form labels under each heading are visually presented as 鈥淪treet鈥, 鈥淭own鈥, etc., hence a VoiceOver user may find it difficult to distinguish between whether these refer to the home or delivery addresses. By applying additional context to each label through the accessibilityLabel
property, we can distinguish between these types of addresses for VoiceOver users.
Android
Android currently does not provide a mechanism to group related controls, except for radio buttons (which are grouped using the RadioGroup
class). However, it is possible to highlight related (non-radio button) form controls by placing a TextView
containing a group label above the list of controls, and then applying the android:contentDescription
attribute to each individual form control within the group to provide more context to a TalkBack user.
Android Pass Example
Radio button components grouped together via a RadioGroup:
<RadioGroup android:layout_width="match_parent" android:id="@+id/radioGroup_1" android:orientation="horizontal">
<RadioButton android:layout_height="wrap_content" android:id="@+id/radio_0" android:checked="true" android:text="Option A" android:contentDescription="Choose Option - Option A"></RadioButton>
<RadioButton android:layout_height="wrap_content" android:id="@+id/radio_1" android:text="Option B" android:contentDescription="Choose Option - Option B"></RadioButton>
<RadioButton android:layout_height="wrap_content" android:id="@+id/radio_2" android:text="Option C" android:contentDescription="Choose Option - Option C"></RadioButton>
</RadioGroup>
Textview example:
<TextView
android:layout_width="match_content"
android:layout_heigth="wrap_content"
android:text="Home address"
android:contentDescription="Home address, heading"/>
<TextView
android.layout_width="wrap_content"
android.layout_height="wrap_content"
android:text="Street"
android:labelFor="@+id/homeStreet"
android:contentDescription="Home street"/>
<EditText
android:id="@+id/homeStreet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"/>
<TextView
android.layout_width="wrap_content"
android.layout_height="wrap_content"
android:text="Town"
android:labelFor="@+id/homeTown"
android:contentDescription="Home town"/>
<EditText
android:id="@+id/homeTown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"/>
鈥
<TextView
android:layout_width="match_content"
android:layout_heigth="wrap_content"
android:text="Delivery address"
android:contentDescription="Delivery address, heading"/>
<TextView
android.layout_width="wrap_content"
android.layout_height="wrap_content"
android:text="Street"
android:labelFor="@+id/deliveryStreet"
android:contentDescription="Delivery street"/>
<EditText
android:id="@+id/deliveryStreet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"/>
<TextView
android.layout_width="wrap_content"
android.layout_height="wrap_content"
android:text="Town"
android:labelFor="@+id/deliveryTown"
android:contentDescription="Delivery town"/>
<EditText
android:id="@+id/deliveryTown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"/>
HTML
Use the fieldset
and legend
elements to semantically group together related form controls.
HTML Pass Example
Text inputs
<fieldset>
<legend>Delivery Address</legend>
<label for="deliveryHouseNumber">House number:</label>
<input type="text" id="deliveryHouseNumber">
<br/>
<label for="deliveryStreetAddress">Street address:</label>
<input type="text" id="deliveryStreetAddress">
<br/>
<label for="deliveryTown">Town:</label>
<input type="text" id="deliveryTown">
<br/>
</fieldset>
Radio buttons
<fieldset>
<legend>Do you like football?</legend>
<input type="radio" id="yesFootball" value="yes" name="football">
<label for="yesFootball">Yes</label>
<br/>
<input type="radio" id="noFootball" value="no" name="football">
<label for="noFootball">No</label>
</fieldset>
Checkboxes
<fieldset>
<legend>Select your favourite sports:</legend>
<input type="checkbox" id="football" name="sports" value="football">
<label for="football">Football</label>
<br/>
<input type="checkbox" id="rugby" name="sports" value="rugby">
<label for="rugby">Rugby</label>
<br/>
<input type="checkbox" id="golf" name="sports" value="golf">
<label for="golf">Golf</label>
<br/>
<input type="checkbox" id="cricket" name="sports" value="cricket">
<label for="golf">Cricket</label>
</fieldset>
Custom form controls
Use WAI-ARIA grouping markup for custom form controls, as follows (Note: use role="radiogroup" for groups of custom radio buttons, and role="group" for all other types of controls):
<p id="groupLabel" tabindex="-1">Do you like football?</p>
<div role="radiogroup" aria-labelledby="groupLabel">
<span role="radio" aria-checked="false" tabindex="0" aria-labelledby="groupLabel yesFootball">
<img src="unchecked.png" alt="">
</span>
<span id="yesFootball" tabindex="-1">Yes</span>
<span role="radio" aria-checked="false" tabindex="-1" aria-labelledby="groupLabel noFootball">
<img src="unchecked.png" alt="">
</span>
<span id="noFootball" tabindex="-1">No</span>
</div>
Testing
Procedures
- Activate a screen reader.
- Locate any forms within the screen.
- Determine if one or more logical groupings exist within the form.
- For each grouping, navigate to each field in the group and verify that the group name is announced prior to the field鈥檚 label.
- Verify that the methods of interacting with each grouping work as expected with alternative input methods.
Outcome
The following checks are all true:
- On-screen fields that are part of a logical grouping have a visible group name indicated as part of the label for the on-screen field;
- For each field that is part of the group, the group label is announced prior to the field鈥檚 label by either using platform conventions to associate fields with a group and testing using a screen reader, or pre-pending the group label to the accessible name of each field within the group;
- For each group of items, navigation and interaction among the group items must work as expected for group items, for example, properly grouped HTML radio buttons allow navigation between them via up and down arrows.