Skip to content

Fieldset

<fieldset> with <legend> is the one way to give a set of radio buttons or checkboxes a shared label that screen readers announce before each option. A heading above the group looks the same, but is not heard.

In practice the component is two things: a reset of the browser’s border from 1996, and the same required and optional markers as on an ordinary Label.

Attribute on .lt-legendEffect
(omitted)An ordinary label
data-required="symbol" * in the danger colour
data-required="text" (required) in the danger colour
data-optional="" (optional) in a muted colour

Choose one of data-required and data-optional, and stick to it throughout the form.

The group can have help text and an error text like the fields otherwise. The connection for screen readers goes through aria-describedby on the <fieldset>, not on each button.

data-state="invalid" on the group colours the label. The buttons themselves must get the same state one by one, since they are the ones with the border the user sees. They also need aria-invalid for the screen reader, and lt.radio({ state: "invalid" }) gives both.

data-state="success" colours the label green in the same way, and is used when the group is filled in and accepted. The colour is the only difference between the two, so it cannot stand alone: say in the text what is wrong or what is in order.

disabled on the <fieldset> disables everything inside, including fields added later:

<fieldset class="lt-fieldset" disabled>
<legend class="lt-legend" aria-disabled="true">Delivery method</legend>
</fieldset>

Note that disabled on the group does not set disabled on each control. The property button.disabled is still false, while button.matches(":disabled") is true. If you check in JavaScript, use the latter.

ClassWhat it is
.lt-fieldsetThe group. The state comes from data-state.
.lt-legendThe label of the group
VariableDefault
--lt-fieldset-gapvar(--lt-spacing-2)
--lt-label-required-symbol" *"
--lt-label-required-text" (required)"
--lt-label-optional-text" (optional)"

The last three are the text of the marker behind data-required and data-optional on .lt-legend. They are the same as on Label, so one setting applies to both. See Translation.

See Customising for how the variables and the layer work.

  • <legend> should be the first element inside the <fieldset>. HTML requires it, and the markup is otherwise invalid. Browsers usually connect the first <legend> to the group wherever it is, but that is nothing to rely on: outside the content model, browsers and assistive technology can behave differently.
  • Use the group where the options belong to the same question. A single checkbox for “I accept the terms” does not need it.
  • Do not put the whole form in one group. The label is then announced before every single field.
  • The group’s error message should say what the user has to do: “Choose how you want the item delivered”, not “This field is required”.