Grouping form controls with headings
It sometimes feels necessary to group complex forms visually using headings. As traditionally used headings are non-focusable elements, you have to make sure that they are not missed by screen reader users in focus mode.
Although it is the traditional way to group form controls using <fieldset>
/<legend>
structures (and they are even capable of being nested), sometimes there is the need to use headings within forms. But those headings are not announced in focus mode by default (if you haven't done this yet, go back and read Screen readers' browse and focus modes).
The good news: since HTML 5.2, headings are allowed within <legend>
elements, see The legend element (W3.org).
Headings mixed into a form as children of legends
<h1>Tell us something about you</h1>
<p>Please fill out the following form.</p>
<form>
<fieldset>
<legend>
<h2>General information</h2>
</legend>
<div class="control">
<label for="name">Full name</label><input id="name" type="text" />
</div>
<div class="control">
<label for="biography">Biography</label
><textarea id="biography"></textarea>
</div>
<fieldset>
<legend>Gender</legend>
<div class="control">
<input id="gender_male" name="gender" type="radio" /><label
for="gender_male"
>Male</label
>
</div>
<div class="control">
<input id="gender_female" name="gender" type="radio" /><label
for="gender_female"
>Female</label
>
</div>
</fieldset>
</fieldset>
<fieldset>
<legend>
<h2>Additional information</h2>
</legend>
<div class="control">
<label for="idol">Idol</label
><select id="idol" size="2">
<option>Michael Jackson</option>
<option>Jesus</option>
<option>Mahatma Gandhi</option>
<option>John Doe</option>
</select>
</div>
<div class="control">
<label for="favourite_car">Favourite Car</label
><select id="favourite_car">
<optgroup label="Swedish Cars">
<option>Volvo</option>
<option>Saab</option>
</optgroup>
<optgroup label="German Cars">
<option>Mercedes</option>
<option>Audi</option>
</optgroup>
</select>
</div>
<div class="control">
<input id="accept_agbs" type="checkbox" /><label for="accept_agbs"
>I accept the terms and conditions</label
>
</div>
<div class="control">
<input type="submit" value="Register" />
</div>
</fieldset>
</form>
.control,
fieldset {
margin: 6px 0;
}
label {
display: inline-block;
width: 120px;
vertical-align: top;
}
input + label {
width: auto;
}
Category |
Result |
Date |
Keyboard only |
✔ (pass) pass
|
- |
2018-5-14 |
NVDA 2018.1 + FF Quantum 59.0.2 |
✔ (pass) pass
|
- |
2018-5-8 |
NVDA 2021.2 + Chrome |
✔ (pass) pass
|
- |
2021-2-10 |
NVDA 2023.1 + Edge |
✔ (pass) pass
|
- |
2023-7-13 |
JAWS 2018.3 + FF ESR 52.7.3 |
✔ (pass) pass
|
- |
2018-5-7 |
JAWS 2021.2 + Chrome |
✔ (pass) pass
|
- |
2021-2-10 |
JAWS 2023.23 + Edge |
✔ (pass) pass
|
- |
2023-7-13 |
By the way, the other way round (placing legends into headings) does not work, because a <legend>
always has to be the first child of its <fieldset>
.
Conclusion
Although it is possible to mix headings into forms, you should be careful with that. For example, when a heading is announced as part of a <fieldset>
's <legend>
, its level (<h1>
, <h2>
, etc.) is omitted, which then could lead to confusion.
So in general, try to keep your forms as easy as possible. It often is better to split a complex form into different steps than displaying it on one single page.