# Fieldset

Related fields under one engraved legend, with one hint and one error line for the group. It is the browser’s own fieldset and legend, so the group has a name for a screen reader and a disabled fieldset disables every field inside. The legend runs on into a groove cut in the plate; the error waits at the foot, not drawn, until it has something to say.

Kit part · Free · https://tactileui.dev/parts/fieldset

- Element: `<fieldset class="tui-fieldset">`
- Needs: `kit.css` and `kit.mjs` ([how to install the kit](https://tactileui.dev/docs/installation#the-kit))
- Categories: Forms
- Finishes: Aluminium, Graphite, Oak, Walnut, Porcelain, Black, Daylight, Night, Paper, Phosphor, E-paper, AMOLED
- Volumes: Tactile, Quiet

## Variants

### Plain · legend, fields

```html
<div style="width:min(280px,100%)"><fieldset class="tui-fieldset" aria-describedby="fs36-error"><legend class="tui-fieldset__legend">Delivery</legend><div class="tui-fieldset__fields"><div class="tui-field"><label class="tui-label" for="fi34">Street</label><input class="tui-input" id="fi34" type="text" value="12 Quay Road" tabindex="-1"></div><div class="tui-field"><label class="tui-label" for="fi35">Town</label><input class="tui-input" id="fi35" type="text" value="Porto" tabindex="-1"></div></div><p class="tui-fieldset__error" id="fs36-error" role="alert"></p></fieldset></div>
```

### With a hint

```html
<div style="width:min(280px,100%)"><fieldset class="tui-fieldset tui-fieldset--choices" aria-describedby="fs39-hint fs39-error"><legend class="tui-fieldset__legend">Send me alerts for</legend><p class="tui-fieldset__hint" id="fs39-hint">Pick the events that should send an email.</p><div class="tui-fieldset__fields"><label class="tui-choice"><input class="tui-check" type="checkbox" name="fc37" value="failed-payments" tabindex="-1"> Failed payments</label><label class="tui-choice"><input class="tui-check" type="checkbox" name="fc38" value="refunds" tabindex="-1"> Refunds</label></div><p class="tui-fieldset__error" id="fs39-error" role="alert"></p></fieldset></div>
```

### With an error

```html
<div style="width:min(280px,100%)"><fieldset class="tui-fieldset tui-fieldset--choices" data-error aria-describedby="fs42-hint fs42-error"><legend class="tui-fieldset__legend">Send me alerts for</legend><p class="tui-fieldset__hint" id="fs42-hint">Pick the events that should send an email.</p><div class="tui-fieldset__fields"><label class="tui-choice"><input class="tui-check" type="checkbox" name="fc40" value="failed-payments" tabindex="-1"> Failed payments</label><label class="tui-choice"><input class="tui-check" type="checkbox" name="fc41" value="refunds" tabindex="-1"> Refunds</label></div><p class="tui-fieldset__error" id="fs42-error" role="alert">Choose at least one.</p></fieldset></div>
```

### Fields in a row · --row

```html
<div style="width:min(280px,100%)"><fieldset class="tui-fieldset tui-fieldset--row" aria-describedby="fs45-error"><legend class="tui-fieldset__legend">Billing contact</legend><div class="tui-fieldset__fields"><div class="tui-field"><label class="tui-label" for="fi43">Name</label><input class="tui-input" id="fi43" type="text" value="Mara Voss" tabindex="-1"></div><div class="tui-field"><label class="tui-label" for="fi44">Email</label><input class="tui-input" id="fi44" type="email" value="mara@northwind.example" tabindex="-1"></div></div><p class="tui-fieldset__error" id="fs45-error" role="alert"></p></fieldset></div>
```

### On its own tray · --well

```html
<div style="width:min(280px,100%)"><fieldset class="tui-fieldset tui-fieldset--well tui-fieldset--choices" aria-describedby="fs48-hint fs48-error"><legend class="tui-fieldset__legend">Plan</legend><p class="tui-fieldset__hint" id="fs48-hint">Billed monthly.</p><div class="tui-fieldset__fields"><label class="tui-choice"><input class="tui-radio" type="radio" name="fp46" tabindex="-1" checked> Team</label><label class="tui-choice"><input class="tui-radio" type="radio" name="fp47" tabindex="-1"> Scale</label></div><p class="tui-fieldset__error" id="fs48-error" role="alert"></p></fieldset></div>
```

### Disabled

```html
<div style="width:min(280px,100%)"><fieldset class="tui-fieldset" disabled aria-describedby="fs50-hint fs50-error"><legend class="tui-fieldset__legend">Delivery</legend><p class="tui-fieldset__hint" id="fs50-hint">Locked while the order is packed.</p><div class="tui-fieldset__fields"><div class="tui-field"><label class="tui-label" for="fi49">Street</label><input class="tui-input" id="fi49" type="text" value="12 Quay Road" tabindex="-1"></div></div><p class="tui-fieldset__error" id="fs50-error" role="alert"></p></fieldset></div>
```

## Every state

- Rest
- With a hint
- Error
- Disabled

Hover, pressed and focus are forced with `.is-hover`, `.is-down` and `.is-focus`; checked and disabled are real.

## API

| Name | Kind | Values | What it does |
| - | - | - | - |
|  | element | with aria-describedby | The group. List the ids of the hint and then the error in aria-describedby, and both are read as the focus enters. |
|  | element | first child | The group’s name, printed small with the engraved rule running on from it. Keep it a few words. |
| .tui-fieldset__hint · .tui-fieldset__error | class | paragraphs with ids | How to fill the group in, and what is wrong with it. The error is empty until it has words and has role="alert", so it is read out when it appears. |
| .tui-fieldset__fields | class | around the fields | A grid with a gap. Add --row to the fieldset to set fields side by side, --choices for a tighter column of checks or radios, --well to set the group on a tray. |
| data-min · data-max | attribute | on the fieldset, numbers | How many checkboxes must be down. The form is stopped on submit if the count is out of range; the next change re-checks it. |
| data-error | attribute | set by the script | Marks the group as wrong: the legend’s rule turns red. Add it yourself when you set the error with your own code. |
| fieldsetError(fieldset, words) | function | a fieldset, a string or empty | Puts words in the error line, or takes them away, and marks the fieldset. |
| fieldsetValidate(fieldset) · fieldsetCheck(count, { min, max }) | function | kit.mjs | Checks a fieldset that has a count and returns whether it is right; the words for a count, pure. |

## Markup

```html
<fieldset class="tui-fieldset tui-fieldset--choices" aria-describedby="alerts-hint alerts-error" data-min="1">
  <legend class="tui-fieldset__legend">Send me alerts for</legend>
  <p class="tui-fieldset__hint" id="alerts-hint">Pick the events that should send an email.</p>
  <div class="tui-fieldset__fields">
    <label class="tui-choice"><input class="tui-check" type="checkbox" name="alert" value="failed"> Failed payments</label>
    <label class="tui-choice"><input class="tui-check" type="checkbox" name="alert" value="refunds"> Refunds</label>
  </div>
  <p class="tui-fieldset__error" id="alerts-error" role="alert"></p>
</fieldset>

<!-- Fields in a row, on their own tray -->
<fieldset class="tui-fieldset tui-fieldset--row tui-fieldset--well">
  <legend class="tui-fieldset__legend">Billing contact</legend>
  <div class="tui-fieldset__fields"> <div class="tui-field">…</div> <div class="tui-field">…</div> </div>
</fieldset>
```

```js
import { fieldsetError, fieldsetValidate, fieldsetCheck } from '/kit/kit.mjs';

// data-min and data-max on the fieldset are checked on submit and on change. Or say it yourself:
fieldsetError(document.querySelector('#alerts'), 'Choose at least one.');
fieldsetError(document.querySelector('#alerts'), '');      // take it away

fieldsetValidate(fieldset);                  // true when the count is right
fieldsetCheck(0, { min: 1 });                // 'Choose at least one.'
```

The demo on the page:

```html
<form class="tui-panel" style="width:min(100%,460px);display:grid;gap:26px;text-align:left" action="/parts/fieldset" method="get" data-fs-form><div class="tui-panel__head" style="margin-bottom:0"><span class="tui-label">Account · Northwind billing</span><span class="tui-status"><span class="tui-lamp" data-on></span>Active</span></div>
  <div style="width:min(999px,100%)"><fieldset class="tui-fieldset tui-fieldset--choices" data-min="1" aria-describedby="fs-alerts-hint fs-alerts-error"><legend class="tui-fieldset__legend">Send me alerts for</legend><p class="tui-fieldset__hint" id="fs-alerts-hint">Pick the events that should send an email. Press Save with none ticked to see the error.</p><div class="tui-fieldset__fields"><label class="tui-choice"><input class="tui-check" type="checkbox" name="alert" value="failed-payments"> Failed payments</label><label class="tui-choice"><input class="tui-check" type="checkbox" name="alert" value="refunds"> Refunds</label><label class="tui-choice"><input class="tui-check" type="checkbox" name="alert" value="disputes"> Disputes</label></div><p class="tui-fieldset__error" id="fs-alerts-error" role="alert"></p></fieldset></div>
  <div style="width:min(999px,100%)"><fieldset class="tui-fieldset tui-fieldset--row" aria-describedby="fs-contact-hint fs-contact-error"><legend class="tui-fieldset__legend">Billing contact</legend><p class="tui-fieldset__hint" id="fs-contact-hint">We send invoices here.</p><div class="tui-fieldset__fields"><div class="tui-field"><label class="tui-label" for="fs-name">Name</label><input class="tui-input" id="fs-name" type="text" value="Mara Voss"></div><div class="tui-field"><label class="tui-label" for="fs-mail">Email</label><input class="tui-input" id="fs-mail" type="email" value="mara@northwind.example"></div></div><p class="tui-fieldset__error" id="fs-contact-error" role="alert"></p></fieldset></div>
  <div style="display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap"><output class="tui-muted" style="font-size:var(--tui-t-small)" data-fs-out aria-live="polite">Nothing saved yet</output><button class="tui-key tui-key--primary" type="submit">Save</button></div></form>
```

## Usage notes

Press Save with nothing ticked: the form is stopped, the error line is given its words, the groove under the legend turns red and the focus goes to the first box. Tick one and the error clears by itself. The billing contact has fields in a row.

## Accessibility

- The legend and the fieldset are the browser’s, so a screen reader announces “Send me alerts for, group” as the focus enters it, then the field, with no ARIA added.
- `aria-describedby` on the fieldset has the hint and the error read after the legend. The error line is a `role="alert"`, empty until needed, so it is spoken the moment it is given words.
- The error is never red alone: a lit red lamp and the words, “Choose at least one.”, and the groove under the legend turns red too. The focus is sent to the first box that can be changed.
- The keyboard needs nothing new: Tab goes through the fields in the order they are written, Space ticks a box, and Enter submits as in any form.
- A `disabled` fieldset disables every field in it, which the browser already does; the legend and hint go quieter and the group stays readable.
- The legend is floated so it can carry its rule; it remains a legend in the document, so the group keeps its name. On a phone the fields stack and every box is 44px tall to hit.

## How it’s built

There is one native element and four lines of drawing. The legend is floated so that it stops being the border’s special case and can hold a rule: a flex row with the words and a 2px groove after them, dark with the light on its lower lip, the cut the toolbar and the toggle group already use between clusters. The error is the kit’s red lamp and a line of words. The count check is the only script, and it runs on the form’s own submit and the field’s own change.

## Rules

Name a group of fields once, say how to fill it in once, and say what is wrong with it once, in a place a screen reader will read.

The full rules are in `fieldset.rules.md` in the brain, which ships with the Kit.

## Related parts

- [Field](https://tactileui.dev/parts/field): Text entry in a recess, the label printed above it, and a hint that says how to fix an error.
- [Form validation](https://tactileui.dev/parts/validation): A form that says what is wrong, on the browser’s own checks. Press submit with something missing and each field in error gets a line under it with a red lamp lit and words that say how to put it right, its well ringed red; at the top of the form an annunciator lights over a count and a list that links to each field, and takes the focus. From then on each field is checked again as it changes.
- [Check](https://tactileui.dev/parts/check): An option in a form: a small square key that presses below the plate when chosen, with a tick printed on it.
- [Radio](https://tactileui.dev/parts/radio): One choice from a short list in a form: small round keys in a fieldset, the chosen one pressed with a printed dot.
