Grouping form controls in a meaningful way can make them much easier to handle for everyone. While most controls can be grouped, some must be grouped. The fieldset/legend structure is available for exact that - it can even be nested. And if its visual limitations are a problem, ARIA can be of help.
<fieldset>/<legend> structures are used for grouping form controls that are related in some way. While in complex forms they can be used to group all kinds of form controls, groups of radio buttons and checkboxes should always be grouped by them.
Grouping any kinds of controls
Grouping thematically
The more complex forms are, the more it makes sense to group thematically related form controls of any kind.
<form><fieldset><legend>Your address</legend><divclass="control"><labelfor="first_name">First name</label><inputid="first_name"type="text" /></div><divclass="control"><labelfor="last_name">Last name</label><inputid="last_name"type="text" /></div><divclass="control"><labelfor="street">Street</label><inputid="street"type="text" /></div><divclass="control"><labelfor="city">City</label><inputid="city"type="text" /></div></fieldset><fieldset><legend>Additional details</legend><divclass="control"><labelfor="biography">Biography</label><textareaid="biography"></textarea></div><divclass="control"><labelfor="image">Image</label><inputid="image"type="file" /></div><divclass="control"><labelfor="age_group">Age Group</label><selectid="age_group"><option>
0 to 9 Years
</option><option>
10 to 19 Years
</option><option>
20 to 29 Years
</option><option>
30 to 39 Years
</option><option>
40 to 49 Years
</option><option>
50 to 59 Years
</option><option>
60 to 69 Years
</option><option>
70 to 79 Years
</option><option>
80 to 89 Years
</option><option>
90 to 99 Years
</option></select></div></fieldset><fieldset><legend>Interests</legend><divclass="control"><labelfor="hobbies">Hobbies</label><selectid="hobbies"multiple=""><option>
Playing soccer
</option><option>
Dancing
</option><option>
Gardening
</option><option>
Watching movies
</option></select></div><divclass="control"><labelfor="favourite_car">Favourite car?</label><selectid="favourite_car"><optgrouplabel="Swedish Cars"><option>
Volvo
</option><option>
Saab
</option></optgroup><optgrouplabel="German Cars"><option>
Mercedes
</option><option>
Audi
</option></optgroup></select></div><divclass="control"><labelfor="public_transports">Favourite public transport?</label><selectid="public_transports"multiple=""><optgrouplabel="Ground"><option>
Train
</option><option>
Bus
</option></optgroup><optgrouplabel="Water"><option>
Ship
</option><option>
Submarine
</option></optgroup><optgrouplabel="Air"><option>
Plane
</option><option>
Cable Car
</option></optgroup></select></div></fieldset></form>
By the way, it is not mandatory to group all your form controls. In the example above, you could easily drop the grouping of interests (or any other). In general: use groupings of thematically related controls whenever it feels meaningful to you and your users.
Grouping to differentiate
Another use case is differentiating groups with similar input fields, for example to tell apart shipping and billing address.
Groups of radio buttons are used as possible answers to a certain question, and as such they are always related to each other. Hence they are always grouped, while the <fieldset>'s <legend> contains the question and each control's <label> contains an answer.
In contrast to radio buttons though, checkboxes do not imperatively relate to each other. So there may be situations where a number of non-related checkboxes can stand on their own, without being grouped:
<form><divclass="control"><inputid="pizza"type="checkbox" /><labelfor="pizza">I like pizza.</label></div><divclass="control"><inputid="weather"type="checkbox" /><labelfor="weather">The weather is sunny.</label></div><divclass="control"><inputid="info"type="checkbox" /><labelfor="info">I would like to receive more information about this.</label></div></form>
.control {
margin: 6px0;
}
Category
Result
Comments
Date
Keyboard only
✔ (pass) pass
-
2018-5-14
NVDA 2023.1 + FF 115
✔ (pass) pass
-
2023-8-3
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
This is also reflected by the fact that each checkbox's name attribute is different.
Notice: single checkboxes, like "Accept terms and conditions", do not need a <fieldset>/<legend>. Still, it can make perfect sense to put them into their own <fieldset>/<legend>, maybe together with some related link(s) or text (see Placing non-interactive content between form controls).
Nesting
Nesting <fieldset>/<legend> structures is possible, especially when groups of radio buttons or checkboxes play a role.
But you should not overdo that: while screen readers uniformly announce when a grouping is entered, some do not announce when it is exited. As such, too much nesting can quickly become confusing.
Keeping legends short
Regarding <fieldset>/<legend> structures, some screen readers are much wordier than others.
On one side, JAWS attaches the <legend>'s text to the label of each contained form control. For a <legend> with the text "Personal Details" and two fields "First Name" and "Last Name", it literally announces:
Personal Details, First Name.
Personal Details, Last Name.
On the other side, NVDA only announces the <legend>'s text when entering the <fieldset>. For the above example, it literally announces:
Personal Details, Grouping. First Name.
Last Name.
Update 2023: JAWS and NVADA attaches the <legend> only once by focusing the first element 'First Name" or entering the <fieldset>.
<form><fieldset><legend>Please fill out the following inputs truthfully and completely. We will get back to you as soon as possible. Thank you for your effort.</legend><divclass="control"><labelfor="name">Full name</label><inputid="name"type="text" /></div><divclass="control"><labelfor="biography">Biography</label><textareaid="biography"></textarea></div><divclass="control"><labelfor="age_group">Age Group</label><selectid="age_group"><option>
0 to 9 Years
</option><option>
10 to 19 Years
</option><option>
20 to 29 Years
</option><option>
30 to 39 Years
</option><option>
40 to 49 Years
</option><option>
50 to 59 Years
</option><option>
60 to 69 Years
</option><option>
70 to 79 Years
</option><option>
80 to 89 Years
</option><option>
90 to 99 Years
</option></select></div></fieldset></form>
As a result, some screen readers seem to have difficulties associating the <legend> correctly to its <fieldset> if it is not its first child.
Overcoming visual limitations using ARIA
Admittedly, browsers have a quite peculiar opinion on how to visually style <fieldset>/<legend> structures, and it is quite hard to override their preferences.
So if for visual (or any other serious) reason you cannot use standard <fieldset>/<legend>, you may take ARIA to the rescue:
Use role="group" to give a <div> container the semantics of a <fieldset>.
Associate any other text to the grouping container using aria-describedby.
JAWS only says Male but not the fieldset-name -gendre-
2023-7-13
As always, we highly recommend to use traditional solutions over ARIA, so if you haven't done this yet, go back and read ARIA - when HTML simply is not enough.