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 with legend,
  • 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

  1. Activate a screen reader.
  2. Locate any forms within the screen.
  3. Determine if one or more logical groupings exist within the form.
  4. For each grouping, navigate to each field in the group and verify that the group name is announced prior to the field鈥檚 label.
  5. 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.