Accessible form structure

Group related controls

Use fieldset and legend to give checkbox and radio groups a question that remains available to screen-reader users.

A label names one control. A set of related controls needs a name too, and that’s what fieldset and legend are for.

Think of a question with several answers:

<fieldset>
  <legend>Preferred contact method</legend>

  <label>
    <input name="contact" type="radio" value="email">
    Email
  </label>

  <label>
    <input name="contact" type="radio" value="phone">
    Phone
  </label>
</fieldset>

The legend is the question. Each label is an answer. When a screen reader user moves into the group, they hear something like “Preferred contact method, Email, radio button, 1 of 2”. Both pieces arrive together.

Notice that legend must be the first child of the fieldset. Put it anywhere else and browsers won’t treat it as the group’s name.

Why proximity isn’t enough

Visually, a heading above a row of radio buttons looks grouped. Nobody sighted is confused.

But a screen reader moves control by control. Without the fieldset, focus lands on a radio button that announces “Email, radio button”. Email what? The question is on screen, but it isn’t connected to the control, so it’s never announced.

Don’t wrap everything

A fieldset is for a meaningful group, not a generic section wrapper. A block of independent text fields, like name, email, and company, usually works fine with a heading and normal labels.

Radio buttons almost always need one. A set of related checkboxes, like “Which topics interest you?”, usually does too. If the controls answer one shared question, group them.

Disable a whole group

A fieldset has one more trick. Add disabled to it and every control inside becomes disabled at once:

<fieldset disabled>
  <legend>Shipping address</legend>
  ...
</fieldset>

This is handy for a section that only applies when another option is selected. Remember from earlier that disabled controls are not submitted, so the server won’t receive any of these fields.

Errors on a group

When the whole group is invalid, say a required radio group with nothing selected, put one error message near the legend. Connect it to the fieldset with aria-describedby if you need to. Don’t repeat the same message under every option, which turns one problem into five.

Try this on your own form: navigate a radio group with only the keyboard. Tab should land on the group once. Arrow keys should move between the options. If Tab stops on every radio button, the buttons don’t share a name and aren’t a group at all.

Lesson completed