ESC

Type to search the knowledge base.

fieldset and legend

Group related form controls with fieldset/legend — radio groups, disabled fieldsets, and accessible naming for control sets.

beginner3 min read
  • html
  • fieldset
  • forms

<fieldset> groups related controls; <legend> captions that group. Assistive tech announces the legend with each control in the set, which is the correct pattern for radio groups and multi-part questions.

Docs: MDN <fieldset>, <legend>, WAI forms.

Radio group (canonical)

<fieldset>
  <legend>Shipping method</legend>

  <div class="choice">
    <input type="radio" id="ship-standard" name="shipping" value="standard" checked />
    <label for="ship-standard">Standard (3–5 days)</label>
  </div>

  <div class="choice">
    <input type="radio" id="ship-express" name="shipping" value="express" />
    <label for="ship-express">Express (1–2 days)</label>
  </div>
</fieldset>
  • Same name on radios → one selection.
  • Legend names the question; label names each option.
  • Don’t replace this with a lone div + aria-label unless you know why.

Checkbox groups and address blocks

<fieldset>
  <legend>Notify me about</legend>
  <label><input type="checkbox" name="notify" value="product" /> Product updates</label>
  <label><input type="checkbox" name="notify" value="blog" /> Blog posts</label>
</fieldset>

<fieldset>
  <legend>Billing address</legend>
  <label for="line1">Address line 1</label>
  <input id="line1" name="line1" autocomplete="billing address-line1" />
  <!-- more fields -->
</fieldset>

disabled fieldset

<fieldset disabled>
  <legend>Enterprise options</legend>
  <!-- all controls inside disabled -->
</fieldset>

Disabling the fieldset disables its descendants (with some historical quirks on legacy browsers). Useful for “upgrade to unlock” sections.

Styling without breaking a11y

fieldset {
  border: 1px solid var(--border);
  border-radius: 0.5rem;
  padding: 1rem 1rem 0.75rem;
  margin: 0;
  min-inline-size: 0;
}

legend {
  padding-inline: 0.35rem;
  font-weight: 600;
}

Avoid display: contents on fieldset/legend in older engines — it historically broke accessibility mappings. Test if you reset fieldset display aggressively.

Nested fieldsets

Allowed but easy to over-nest. Prefer flatter forms; nest only when subgroups are real (e.g., “Card details” inside “Payment”).

Interview out-loud

“fieldset groups related controls and legend provides the group’s accessible name — essential for radio questions. Shared name on radios handles mutual exclusion. I can disable a whole group with disabled on the fieldset. I style carefully so legend semantics remain intact.”

Footguns

  1. Using a heading instead of legend for radio groups (weaker association).
  2. Empty legend or legend far from controls visually without structure.
  3. Forgetting shared name on radios.
  4. display: contents on fieldset breaking AT in some browsers.
  5. Disabling fieldset and wondering why submit buttons inside won’t click — intentional.

Error messaging for groups

<fieldset aria-describedby="shipping-error" aria-invalid="true">
  <legend>Shipping method</legend>
  <!-- radios -->
  <p id="shipping-error" class="error" role="alert">Choose a shipping method.</p>
</fieldset>

Associate group-level errors with the fieldset via aria-describedby. Focus the first radio on submit if invalid. Individual fields still need their own labels.

Styling legend

Legends are awkward to lay out historically. Prefer simple left-aligned legends over absolute positioning that breaks zoom. If you visually hide a legend, provide another accessible name — do not remove the legend without a replacement.

Checkbox lists vs radiogroups

Radios require a shared name and almost always a fieldset/legend question. Checkbox lists may use a fieldset when the group is a multi-select answer to one question (“Notify me about”). Independent settings toggles can be separate labeled checkboxes without a fieldset. Choose based on whether the controls form one question or many unrelated prefs.

<fieldset>
  <legend>Add-ons</legend>
  <label><input type="checkbox" name="addon" value="gift" /> Gift wrap</label>
  <label><input type="checkbox" name="addon" value="rush" /> Rush handling</label>
</fieldset>

Further reading

Related guides