HTMLGrouping (<fieldset>, <legend>)

Grouping Fields with <fieldset> and <legend>

Forms are often more than a flat list of inputs — a shipping form has an address section and a payment section; a survey has groups of related radio buttons. <fieldset> draws a semantic (and, by default, visual) boundary around a group of related controls, and <legend> gives that group a caption. Together they turn "just a bunch of inputs" into a form with real structure.

Basic Usage

fieldset-basic.html

HTML
<form>
  <fieldset>
    <legend>Shipping Address</legend>

    <label for="street">Street</label>
    <input type="text" id="street" name="street" />

    <label for="city">City</label>
    <input type="text" id="city" name="city" />

    <label for="zip">ZIP code</label>
    <input type="text" id="zip" name="zip" />
  </fieldset>

  <fieldset>
    <legend>Payment Details</legend>

    <label for="card-number">Card number</label>
    <input type="text" id="card-number" name="card-number" />
  </fieldset>
</form>
Legend must come first
The <legend> must be the first child inside its <fieldset> for browsers and assistive technology to treat it as the group's caption. Anything else goes after it.
The Classic Use Case: Radio & Checkbox Groups

The single most useful pattern for <fieldset>/<legend> is grouping a set of radio buttons or checkboxes that represent one logical question. Without it, a screen reader announces each option's label individually with no shared context — the visitor has no idea what the choice is for.

fieldset-radio-group.html

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

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

  <input type="radio" id="contact-phone" name="contact" value="phone" />
  <label for="contact-phone">Phone</label>

  <input type="radio" id="contact-sms" name="contact" value="sms" />
  <label for="contact-sms">SMS</label>
</fieldset>
Why It Matters for Accessibility

Each individual <label> in the group above announces its own text ("Email", "Phone", "SMS") — but none of them say what question is being answered. When the group sits inside a <fieldset> with a <legend>, most screen readers announce the legend text together with each option, effectively reading "Preferred contact method, Email" for the first radio button. That context is easy to lose without it.

  • One <fieldset> per logical question or grouping — not one per field.

  • The <legend> text should describe the group as a whole, not repeat an individual option.

  • Nested <fieldset>s are allowed for sub-groups within a bigger group (e.g. billing address inside a payment fieldset).

Disabling an Entire Group

Setting disabled directly on a <fieldset> disables every form control nested inside it in one shot — a much simpler alternative to disabling each input individually with JavaScript.

fieldset-disabled.html

HTML
<form>
  <fieldset id="billing-fields" disabled>
    <legend>Billing Address</legend>
    <label for="same-as-shipping">
      <input type="checkbox" id="same-as-shipping" checked />
      Same as shipping address
    </label>

    <label for="bill-street">Street</label>
    <input type="text" id="bill-street" name="bill-street" />
  </fieldset>
</form>

<script>
  const checkbox = document.getElementById('same-as-shipping');
  const fieldset = document.getElementById('billing-fields');

  checkbox.addEventListener('change', () => {
    fieldset.disabled = checkbox.checked;
  });
</script>
Disabled fields are not submitted
Fields inside a disabled <fieldset> are excluded from form submission entirely — their values won't appear in the submitted data, even if the input itself has a value. Don't use this to "hide" data you still need sent to the server.
Styling <fieldset> and <legend>

By default, browsers render <fieldset> with a border and inner padding, and <legend> overlapping that border. Both are fully styleable with CSS — a very common pattern is removing the border entirely and using the fieldset purely for its semantic/accessibility value.

fieldset.css

CSS
fieldset {
  border: none;
  padding: 0;
  margin: 0 0 24px;
}

legend {
  font-weight: 600;
  margin-bottom: 8px;
  padding: 0;
}
Not just for forms with many sections
Even a short form benefits from a single fieldset wrapping a group of checkboxes ("Which newsletters do you want?") — anywhere you have more than one related control answering the same underlying question is a good candidate.
Rule of thumb
Ask: "if I read only the individual labels out loud, would the question still make sense?" If not, wrap the group in a <fieldset> with a <legend> describing the shared question.