My previous article Creating Accessible Input Forms in Asp.Net MVC - Section 508 Compliance and ADA Compliance demonstrated how to make an input form accessible to enable screen reader users use our application. Though my previous article covered all scenarios it didn’t speak much about how to make RadibuttonList and CheckBoxList controls accessible which are commonly found in input forms. For instance, a RadioButtonList is commonly used control to select one option from a range of options. Similarly, a CheckBoxList control is used to select multiple options from a range of options.
Moving forward, let’s understand how to make these input elements accessible i.e. let’s make the label and individual option’s label of these controls readable by a screen reader in this article.
Creating Accessible RadioButtonList Control
A very common usage of RadibuttonList control is getting the input for Gender field which will display options like Male, Female and Do not disclose. Generally we will display a label with “for” attribute and list of RadioButton options for selection. Something like below.
<div class="form-group">
<label class="control-label col-md-2" for="Gender">Gender</label>
<div class="col-md-10">
<input data-val="true" data-val-required="The Gender field is required." id="Gender" name="Gender" value="1" class="valid" aria-invalid="false" type="radio">Male
<input id="Gender" name="Gender" value="2" class="valid" type="radio">Female
</div>
</div>
The problem with the above HTML is, the screen reader would read the Gender label correctly but it will not read the label with each options. Refer the below NVDA speech viewer which shows the Male and Female label is not being read.
Let’s see how to solve this and make the screen reader to correctly read the field label and each option’s label when reading the input field. There are 2 ways we can solve this, you can choose one of the approach that suits you.
-
Using <fieldset> and <legend>
This is one the easiest way to read the field label and the options label when using RadioButtonList control. See the below changed HTML that uses <fieldset> and <legend> and additionally it has included the option text into a label control and associated it with the corresponding radio button.
<div class="form-group">
<fieldset>
<legend class="col-md-2">
<label class="control-label pull-right">Gender</label>
</legend>
<div class="col-md-10">
<input id="rdbMale" name="Gender" value="Male" type="radio">
<label for="rdbMale">Male</label>
<input id="rdbFemale" name="Gender" value="Female" type="radio">
<label for="rdbFemale">Female</label>
</div>
</fieldset>
</div>
When using NVDA reader, the field will be now read properly. Refer the below NVDA speech viewer window.
The MVC Razor code is below.
<div class="form-group">
<fieldset>
<legend class="col-md-2">
<label class="control-label pull-right">Gender</label>
</legend>
<div class="col-md-10">
@Html.RadioButton("Gender", "Male", new { id = "rdbMale" })
@Html.Label("Male", new { @for = "rdbMale" })
@Html.RadioButton("Gender", "Female", new { id = "rdbFemale" })
@Html.Label("Female", new { @for = "rdbFemale" })
</div>
</fieldset>
</div>
Please note that including fieldset and legend may require a CSS style change to make the form look like before without using fieldset.
-
Using ARIA Role attribute
The other way of addressing RadioButtonList to make the screen reader understand is by using ARIA role attribute. The solution is to set role=”radiogroup” and an aria-label to the container that contains the RadioButtonList control. The below code does that.
<div class="form-group" role="radiogroup" aria-label="Gender">
<label class="control-label col-md-2">Gender</label>
<div class="col-md-10">
<input id="rdbMale" name="Gender" value="Male" type="radio">
<label for="rdbMale">Male</label>
<input id="rdbFemale" name="Gender" value="Female" type="radio">
<label for="rdbFemale">Female</label>
</div>
</div>
The NVDA screen reader output can be seen below.
The MVC Razor code is below.
<div class="form-group" role="radiogroup" aria-label="Gender">
<label class="control-label col-md-2">Gender</label>
<div class="col-md-10">
@Html.RadioButton("Gender", "Male", new { id = "rdbMale" })
@Html.Label("Male", new { @for = "rdbMale" })
@Html.RadioButton("Gender", "Female", new { id = "rdbFemale" })
@Html.Label("Female", new { @for = "rdbFemale" })
</div>
</div>
This solution is better for situations when used for a custom RadioButon control i.e. when you want to provide a RadioButton function with a custom HTML element. For example, you may want to display a div tag with text “Select“ as a clickable button for “unchecked” state which can be changed to “Selected” text when it is clicked for a “checked” state using JavaScript instead of using a native radio button. When reading a native RadioButton control, the reader knows it is a radio button only when the type is set to radio(type=”radio”). So, when it is customized like I mentioned above it needs to be hinted with role attribute on container level and each radio button alternate(div in our case) should be set with role=”radio”. Please note, in our previous code, each RadioButton are already native RadioButton control so it does not require role=”Radio” attribute. The custom radio button control with role attributes set for accessibility will look like below.
<div class="form-group" role="radiogroup" aria-label="Gender">
<label class="control-label col-md-2">Gender</label>
<div class="col-md-10">
<div class="radio" value="male" role="radio" tabindex="0">Male</div>
<div class="radio" value="female" role="radio" tabindex="0">Female</div>
<input id="Gender" name="Gender" type="hidden">
</div>
</div>
Creating Accessible CheckBoxList Control
Similar to RadioButtonList control, we will also have CheckBoxList control in input forms. For example, we may have a field to select list of hobbies which lists each hobby in a checkbox. Refer the below figure in Point(i). Let’s see how to make it accessible using fieldset and by using role attribute.
-
Using <fieldset> and <legend>
This works similar to RadioButtonList control under fieldset. The legend text will be read as label for the field and each checkbox control with label will be read separately. The HTML below. Please ensure you are associating each option’s label with the checkbox control with for attribute as in below code.
<div class="form-group">
<fieldset>
<legend class="col-md-2"><label class="control-label col-md-2" for="Hobbies">Hobbies</label></legend>
<div class="col-md-10">
<input id="chkPhotography" name="chkPhotography" value="true" type="checkbox"><input name="chkPhotography" value="false" type="hidden">
<label for="chkPhotography">Photography</label><br>
<input id="chkCricket" name="chkCricket" value="true" type="checkbox"><input name="chkCricket" value="false" type="hidden">
<label for="chkCricket">Cricket</label><br>
<input id="chkTennis" name="chkTennis" value="true" type="checkbox"><input name="chkTennis" value="false" type="hidden">
<label for="chkTennis">Tennis</label><br>
<input id="chkFootball" name="chkFootball" value="true" type="checkbox"><input name="chkFootball" value="false" type="hidden">
<label for="chkFootball">Foot Ball</label><br>
<input id="chkCycling" name="chkCycling" value="true" type="checkbox"><input name="chkCycling" value="false" type="hidden">
<label for="chkCycling">Cycling</label><br>
<input id="chkHiking" name="chkHiking" value="true" type="checkbox"><input name="chkHiking" value="false" type="hidden">
<label for="chkHiking">Hiking</label> <br>
</div>
</fieldset>
</div>
The NVDA screen reader’s Speech Viewer output is below.
The MVC Razor code is below.
<div class="form-group">
<fieldset>
<legend class="col-md-2">@Html.Label("Hobbies", htmlAttributes: new { @class = "control-label col-md-2" })</legend>
<div class="col-md-10">
@Html.CheckBox("chkPhotography", htmlAttributes: new { })
@Html.Label("Photography", htmlAttributes: new { @for = "chkPhotography" })<br />
@Html.CheckBox("chkCricket", htmlAttributes: new { })
@Html.Label("Cricket", htmlAttributes: new { @for = "chkCricket" })<br />
@Html.CheckBox("chkTennis", htmlAttributes: new { })
@Html.Label("Tennis", htmlAttributes: new { @for = "chkTennis" })<br />
@Html.CheckBox("chkFootball", htmlAttributes: new { })
@Html.Label("Foot Ball", htmlAttributes: new { @for = "chkFootball" })<br />
@Html.CheckBox("chkCycling", htmlAttributes: new { })
@Html.Label("Cycling", htmlAttributes: new { @for = "chkCycling" })<br />
@Html.CheckBox("chkHiking", htmlAttributes: new { })
@Html.Label("Hiking", htmlAttributes: new { @for = "chkHiking" }) <br />
</div>
</fieldset>
</div>
-
Using Role attribute
Similar to radiogroup there is no predefined checkboxgroup for the role attribute for using it with CheckBoxList control. There is another value called “group” for role attribute which can be used to group the related controls together. We can use that here. The HTML is.
<div class="form-group" role="group" aria-label="Hobbies">
<label class="control-label col-md-2" for="Hobbies">Hobbies</label>
<div class="col-md-10">
<input id="chkPhotography" name="chkPhotography" value="true" type="checkbox"><input name="chkPhotography" value="false" type="hidden">
<label for="chkPhotography">Photography</label><br>
<input id="chkCricket" name="chkCricket" value="true" type="checkbox"><input name="chkCricket" value="false" type="hidden">
<label for="chkCricket">Cricket</label><br>
<input id="chkTennis" name="chkTennis" value="true" type="checkbox"><input name="chkTennis" value="false" type="hidden">
<label for="chkTennis">Tennis</label><br>
<input id="chkFootball" name="chkFootball" value="true" type="checkbox"><input name="chkFootball" value="false" type="hidden">
<label for="chkFootball">Foot Ball</label><br>
<input id="chkCycling" name="chkCycling" value="true" type="checkbox"><input name="chkCycling" value="false" type="hidden">
<label for="chkCycling">Cycling</label><br>
<input id="chkHiking" name="chkHiking" value="true" type="checkbox"><input name="chkHiking" value="false" type="hidden">
<label for="chkHiking">Hiking</label><br>
</div>
</div>
The NVDA reader will read similar to how it read when using fieldset control.
The MVC Razor code for the above CheckBoxList control is below.
<div class="form-group" role="group" aria-label="Hobbies">
@Html.Label("Hobbies", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.CheckBox("chkPhotography", htmlAttributes: new {})
@Html.Label("Photography", htmlAttributes: new { @for = "chkPhotography" })<br />
@Html.CheckBox("chkCricket", htmlAttributes: new { })
@Html.Label("Cricket", htmlAttributes: new { @for = "chkCricket" })<br />
@Html.CheckBox("chkTennis", htmlAttributes: new { })
@Html.Label("Tennis", htmlAttributes: new { @for = "chkTennis" })<br />
@Html.CheckBox("chkFootball", htmlAttributes: new { })
@Html.Label("Foot Ball", htmlAttributes: new { @for = "chkFootball" })<br />
@Html.CheckBox("chkCycling", htmlAttributes: new { })
@Html.Label("Cycling", htmlAttributes: new { @for = "chkCycling" })<br />
@Html.CheckBox("chkHiking", htmlAttributes: new { })
@Html.Label("Hiking", htmlAttributes: new { @for = "chkHiking" })<br />
</div>
</div>
Similar to RadioButtonList control, this approach is also best suited when using a custom built CheckBoxList control using JavaScript. For each CheckBox control, we can specify role=”checkbox” if we are not using native CheckBox control for the screen reader to read it as CheckBox.