Note that "appropriately" in this case is semantically meaningful and gives good results when viewed by a screen reader.
Let's say I've got a list of questions, each of which will have the exact same set of responses, formatted something like the following:
.grid {
display: grid;
grid-template-columns: auto repeat(5, 1fr);
max-width: 20em;
}
span {
text-align: center;
}
<div class="grid">
<span></span><span>-2</span><span>-1</span><span>+0</span><span>+1</span><span>+2</span>
<span>Question1</span>
<input type="radio" name="question1" value="-2">
<input type="radio" name="question1" value="-1">
<input type="radio" name="question1" value="0">
<input type="radio" name="question1" value="1">
<input type="radio" name="question1" value="2">
<span>Question2</span>
<input type="radio" name="question2" value="-2">
<input type="radio" name="question2" value="-1">
<input type="radio" name="question2" value="0">
<input type="radio" name="question2" value="1">
<input type="radio" name="question2" value="2">
<span>Question3</span>
<input type="radio" name="question3" value="-2">
<input type="radio" name="question3" value="-1">
<input type="radio" name="question3" value="0">
<input type="radio" name="question3" value="1">
<input type="radio" name="question3" value="2">
</div>
Obviously this markup is just good enough to display the grid approximately the way I want to, but it fails to be semantically meaningful or accessible to a screen reader since there's no mechanism to associate the labels in the header row with the corresponding radio button. Nor is there any association between the labels in the first column and the buttons in the corresponding row. The <label>
tag is only meant to apply to a single <input>
, I know, so that's not the right way to go. I think potentially using a <fieldset>
for each row (and styling the <legend>
to appear in the first column maybe?) is the right approach for the rows, but how should I associate the "-2", "-1", "+0", "+1", and "+2" with the radio buttons in their associated columns?
There are two things you could do: use <table>
and aria-label/aria-labelledby
<table>
Each row is a question, each column is a grade between -2 and +2, and in each cell is a possible answer you can give to a question. This is no less, no more than a form of tabular data. So, it should be in a proper HTML table.
By the way, you used CSS grid for layout, so why not make it simpler by using a true <table>
?
IN an ideal world, each <input>
should have its <label>
. But here, especially if you go for a true <table>
, it isn't easy to arrange for it.
It's doable with the visually hidden text technique, but not very practical:
<td>
<label class="visually-hidden" for="question1value0">Question 1, grade 0</label>
<input type="radio" name="question1" value="0" id="question1value0"/>
</td>
<td>
<label class="visually-hidden" for="question1value1">Question 1, grade +1</label>
<input type="radio" name="question1" value="1" id="question1value1"/>
</td>
etc.
As you can see, you have to repeat "Question 1" multiple times, as well as the "-1", "0", "+1" several times. Here the aria-labelledby attribute can be quite useful and makes the code a lot easier. It could look like this:
<table>
<thead>
<tr>
<th> </th>
<th scope="col" id="minus1">-1</th>
<th scope="col" id="zero">0</th>
<th scope="col" id="plus1">+1</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" id="question1">Question 1</th>
<td><input type="radio" name="question1" value="-1" aria-labelledby="question1 minus1" /></td>
<td><input type="radio" name="question1" value="0" aria-labelledby="question1 zero" /></td>
<td><input type="radio" name="question1" value="1" aria-labelledby="question1 plus1" /></td>
</tr>
...
</table>
As you can see, each radio button is associated with two labels, one for the row (the question) and one for the column (the answer).
You may take another route if you don't want to make an HTML table. Something like this:
<fieldset>
<legend><h2>Question 1</h2></legend>
<input type="radio" ... />
<label for="...">-1</label>
<input type="radio" ... />
<label for="...">0</label>
<input type="radio" ... />
<label for="...">+1</label>
As you can see with that solution, you don't need to repeat "Question 1", but you still need to repeat "-1", "0" and "+1".
Nothe the heading, here <h2>
. This will help those who navigate by heading, which is one of the most common and efficient navigation method.
I left you adapting your CSS if you want to go for this solution. You can of course keep using CSS grid, maybe by adding some additional <div>
.
The version with HTML table and aria-labelledby is my proposition to make your form the most accessible it can be.
Those who navigate with tab will hear "Question 1, answer 1", "Question 1, answer 2", "Question 1, answer 3", Question 2, answer 1", etc. in turn. It looks a little verbose, but the advantage is that they always have the full information. There is less risks to choose an unwanted answer, even if it's thedious.
If the whole thing is in a HTML table, it will help a lot figuring out that there are a serie of questions for which the possible answers are always the same. More advanced screen reader users, who usually don't navigate with tab (because there are many more more efficient shortcuts), can navigate iinside the table from cell to cell, vertically and horizontally. They can review the list of questions much faster by going through the first column, quickly do things like set all answers to 0, better appreciate the graduation ranging from -2 to +2, quickly compare the answer they gave between two questions, etc. This makes the whole process of filling the form a lot, a lot, a lot less painful.
Without a real HTML table, those advanced users will have to go through each question and each answer in turn. It's a lot more thedious (just a little less with the headings), and it's useless to say that there are much more chances to get bored and to don't fill everything through to the end. That's why I would recommend the table version, even over the fieldset+legend+heading version.
IF there are no proper labels, here in the form of aria-labelledby attributes, advanced users will still figure it out if you go for the <table>
.
However, those who are using only tab won't get any indication on what each radio button is for.
So in conclusion, by organizing your thing in a <table>
and by giving good labels, you help both beginners and advanced users as most as you can do.