Skip to content
FormSubmit

HTML

HTML fieldset and legend: grouping form fields accessibly

How to use fieldset and legend in HTML forms: grouping radio and checkbox fields, disabled fieldsets, CSS styling quirks and splitting long forms into sections.

5 min readUpdated Oct 9, 2026

<fieldset> groups related form controls and <legend> gives that group a name, which screen readers announce with each control inside it. Use them for every radio button and checkbox group, and for related fields such as an address. Put the legend first inside the fieldset. A disabled fieldset disables, and stops the submission of, every control inside.

This guide covers when to use fieldset and legend, the disabled trick, how to style them without fighting browser quirks, and using them to split a long form into sections. It's part of the HTML form elements reference.

Basic fieldset and legend

address-form.html
<form action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST">
  <fieldset>
    <legend>Delivery address</legend>

    <label for="street">Street address</label>
    <input id="street" name="street" autocomplete="street-address" required>

    <label for="city">Town or city</label>
    <input id="city" name="city" autocomplete="address-level2" required>

    <label for="postcode">Postcode</label>
    <input id="postcode" name="postcode" autocomplete="postal-code" required>

    <label for="country">Country</label>
    <input id="country" name="country" autocomplete="country-name" required>
  </fieldset>

  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <button type="submit">Continue</button>
</form>

The <legend> must be the first child of the <fieldset>. Put it anywhere else and browsers treat it as ordinary content without the grouping semantics.

Fieldset doesn't change what's submitted. Each input inside still submits under its own name. The grouping is for people and assistive technology.

Why fieldset and legend matter for accessibility

Some questions have two levels of label. Consider:

html
<fieldset>
  <legend>Would you like a follow-up call?</legend>
  <label><input type="radio" name="follow_up" value="yes"> Yes</label>
  <label><input type="radio" name="follow_up" value="no"> No</label>
</fieldset>

Each radio's own label is just "Yes" or "No". Without the fieldset, a screen reader user tabbing into the group hears "Yes, radio button" with no idea what they're agreeing to. With it, they hear the legend first: "Would you like a follow-up call? Yes, radio button, 1 of 2".

That's why the rule is simple: every radio button group and every checkbox group gets a fieldset and legend. A heading or paragraph above the group isn't programmatically linked to it, so it isn't announced.

Don't overdo it. Wrapping every single field in its own fieldset, or nesting fieldsets several levels deep, makes screen readers repeat legends constantly. One level of grouping for related fields is usually right.

A disabled fieldset disables everything inside

Adding disabled to a fieldset disables every form control inside it, with one line:

html
<fieldset id="company-details" disabled>
  <legend>Company details</legend>
  <label for="company">Company name</label>
  <input id="company" name="company" autocomplete="organization">
  <label for="vat">VAT number</label>
  <input id="vat" name="vat_number">
</fieldset>

Two things to know:

  • Disabled controls are not submitted. Their names and values are left out of the form data entirely. That makes a disabled fieldset a clean way to switch off a whole section that doesn't apply.
  • Controls inside the first <legend> stay enabled. This lets you put a toggle in the legend itself.

A common pattern is to enable a section based on an earlier answer:

toggle-section.html
<label>
  <input type="checkbox" id="is-business" name="is_business" value="yes">
  I'm ordering for a business
</label>

<fieldset id="company-details" disabled>
  <legend>Company details</legend>
  <label for="company">Company name</label>
  <input id="company" name="company" autocomplete="organization" required>
</fieldset>

<script>
  const toggle = document.getElementById("is-business");
  const section = document.getElementById("company-details");
  toggle.addEventListener("change", () => {
    section.disabled = !toggle.checked;
  });
</script>

While the fieldset is disabled, the required field inside it doesn't block submission, because disabled controls aren't validated either. If you also want to hide the section, toggle the hidden attribute as well.

Styling fieldset and legend

The default look is a grooved border with the legend sitting on the top edge. Most designs remove it. A reset that avoids the common traps:

fieldset.css
fieldset {
  border: 0;
  padding: 0;
  margin: 0 0 1.5rem;
  min-width: 0;
}

legend {
  padding: 0;
  margin-bottom: 0.75rem;
  font-weight: 600;
}

The min-width quirk

By default a fieldset has a minimum width equal to its content's minimum size. A long unbroken value, a wide table or a <select> with long options can push it wider than its container, and the page scrolls sideways on phones. min-width: 0 fixes it. It's the single most useful line in the reset.

Legend positioning quirks

The legend is rendered specially: it sits over the fieldset's top border, and it doesn't behave like a normal block. Some consequences:

  • Padding on the fieldset doesn't push the legend in the way you expect, because the legend sits in the border area.
  • To make the legend look like a normal heading, the reset above is usually enough once the border is gone. If you need precise control, float it: legend { float: left; width: 100%; } and then clear the next element. Floating takes the legend out of its special position and lets it behave like a block.
  • Margins on the legend can be inconsistent between browsers, so prefer margin on the element after it, or use a gap on the fieldset.

Flex and grid on a fieldset

Current browsers support display: flex and display: grid on a fieldset. The rendered legend isn't part of the flex or grid flow, so it stays on top while the controls are laid out:

css
.options {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
}

If you need the legend inside the flow, the float technique above works.

Be careful with display: contents

display: contents on a fieldset removes its box so its children join the parent's layout. It's tempting for grid layouts, but some browsers have dropped elements with display: contents from the accessibility tree, which removes the very grouping you added the fieldset for. Avoid it on fieldsets. Use nested grids or subgrid instead.

Using fieldsets to split a long form into sections

A long form reads better as a series of clearly named sections, each in its own fieldset:

sectioned-form.html
<form action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST">
  <fieldset>
    <legend>About you</legend>
    <label for="name">Full name</label>
    <input id="name" name="name" autocomplete="name" required>
    <label for="email">Email</label>
    <input id="email" type="email" name="email" autocomplete="email" required>
  </fieldset>

  <fieldset>
    <legend>Your event</legend>
    <label for="event-date">Date</label>
    <input id="event-date" type="date" name="event_date" required>
    <label for="guests">Number of guests</label>
    <input id="guests" type="number" name="guests" min="1" max="500" required>
  </fieldset>

  <fieldset>
    <legend>Anything else?</legend>
    <label for="notes">Notes</label>
    <textarea id="notes" name="notes" rows="4"></textarea>
  </fieldset>

  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <button type="submit">Request a quote</button>
</form>

For a multi-step feel without splitting into separate pages, show one fieldset at a time with "Next" and "Back" buttons (type="button" so they don't submit) and toggle the hidden attribute. Keep everything inside one <form> so a single submit sends every section. Note that hidden fields are still validated on submit, so a required field in a hidden step can block submission with an error the visitor can't see. Either validate each step before moving on, or disable the hidden fieldsets that don't apply.

Sending a grouped form

Fieldsets only affect structure and accessibility. Each named field inside is submitted as usual, so the grouped form above arrives as name, email, event_date, guests and notes. Point the form at https://formsubmit.app/f/YOUR_FORM_ID with method="POST" and every submission is stored, spam-filtered and emailed to you. See how to send an HTML form to email for the full setup, HTML input types for the fields inside your groups, or the HTML form elements overview.

Frequently asked questions

What is the difference between fieldset and legend?

A fieldset is the container that groups related form controls. A legend is the caption for that group and must be the first child of the fieldset. Screen readers announce the legend along with each control in the group.

When should I use a fieldset?

Always for radio button groups and checkbox groups, because each option's label alone doesn't say what the question is. Use one for any set of fields that share a context, such as an address. A form with a few unrelated fields doesn't need one.

Does a disabled fieldset submit its fields?

No. The disabled attribute on a fieldset disables every control inside it, except those inside its first legend, and disabled controls are never submitted.

How do I remove the fieldset border?

Set border to 0, padding to 0 and margin to 0 on the fieldset, and padding to 0 on the legend. Also set min-width to 0 so the fieldset can shrink in narrow layouts.

Can I use display flex or grid on a fieldset?

Yes, in current browsers. The legend is rendered separately from the flex or grid content unless you change how it is displayed, so test layouts that move the legend. Avoid display contents on a fieldset, because some browsers then drop the group from the accessibility tree.

Keep reading

Your form backend is 60 seconds away

Sign up with Google or email, create a form, paste the endpoint. Free forever for small sites — no credit card.