Fieldset

A group of related form controls, named by a legend.

import { ... } from '@corndev/ui'
Connection

HTML

No JavaScript required — these classes are the whole component.

<fieldset class="corn-fieldset" data-padding="md" data-variant="default">
  <legend class="corn-fieldset-legend">Server</legend>
  <input class="corn-input" aria-label="Host" />
</fieldset>

Classes

Class
.corn-fieldset
.corn-fieldset-legend

Attributes

AttributeValues
data-paddingnone|xs|sm|md|lg|xl
data-variantunstyled

Vanilla JS

Optional. createFieldset only adds and removes the classes and attributes above, so the HTML is equivalent. Reach for it when you need to change options at runtime.

import { createFieldset } from '@corndev/ui';
import '@corndev/ui/theme';

const el = document.querySelector('#target');
const instance = createFieldset(el, { /* options */ });

Options

OptionTypeDefaultDescription
padding'none'|'xs'|'sm'|'md'|'lg'|'xl''md'Inner spacing.
variant'default'|'unstyled''default'unstyled drops the border and padding while keeping the grouping semantics, for a group that should not look like a box.
disabledbooleanfalseDisables every control inside it. This is the native disabled attribute rather than a class, so it really does disable them.

React

Fieldset — from @corndev/ui-react.

The legend is the legend prop, not a child: <Fieldset legend="Server">. Omitting it renders no <legend> at all rather than an empty one, because an unlabelled group is a legitimate choice and inventing a name would be worse; legend="" renders the element, since an explicit empty label is a different intent.

Props

PropTypeDefaultDescription
legendReact.ReactNodeundefinedNames the group. Omit it for a group that is visual only; nothing is invented in its place.
padding'none'|'xs'|'sm'|'md'|'lg'|'xl''md'Inner spacing.
variant'default'|'unstyled''default'unstyled drops the border and padding while keeping the grouping semantics.
disabledbooleanfalseDisables every control inside it.
classNamestringundefinedMerged with the Corn UI classes.
childrenReact.ReactNodeundefinedThe controls.

A form of any size is a set of groups, and the browser already has an element for that. Fieldset restyles it rather than replacing it, because the two things it does for free are the two things a div with a heading cannot do:

  • a screen reader announces the group and its name when focus enters it
  • disabled on the group disables every control inside it

React usage

import { Fieldset, Input } from '@corndev/ui-react';
import '@corndev/ui/theme';

<Fieldset legend="Server">
  <Input label="Host" />
  <Input label="Port" />
</Fieldset>

The legend is a prop, not a child, so the element order is not something you can get wrong — a <legend> must be the first child of its <fieldset>.

Disabling a whole group

One attribute, and everything inside goes with it:

<Fieldset legend="Authentication" disabled={!enabled}>
  <Input label="Client ID" />
  <Input label="Client secret" />
  <Button>Test connection</Button>
</Fieldset>

This is native behaviour, not a class the library applies: the controls are genuinely disabled, so they leave the tab order and stop submitting values. Nothing needs to thread a disabled prop down to each field.

Worth knowing when you assert on it: a control inside a disabled fieldset still reports input.disabled === false, because that property reflects only its own attribute. input.matches(':disabled') is the question that gets the right answer.

A group that should not look like a box

variant="unstyled" keeps the semantics and drops the border and padding:

<Fieldset legend="Visibility" variant="unstyled">
  <Radio name="visibility" value="public" label="Public" />
  <Radio name="visibility" value="private" label="Private" />
</Fieldset>

Reach for this rather than dropping back to a div — the grouping is worth keeping even when the box is not.

Vanilla

The markup is the component; createFieldset only restyles what is already there.

<fieldset class="corn-fieldset" data-padding="md">
  <legend class="corn-fieldset-legend">Server</legend>
  <input class="corn-input" aria-label="Host" />
</fieldset>
import { createFieldset } from '@corndev/ui';

const group = createFieldset(document.querySelector('fieldset'), { padding: 'lg' });
group.update({ disabled: true });