Skip to content

PlFieldset

A group of controls that answer one question together, with a name on it. It draws no surface. A grouping is not a sheet, and the sheet already exists.

React
tsx
import { PlFieldset, PlTextField } from 'plass-ui';

<PlFieldset legend="Billing address" description="Where the invoice goes.">
  <PlTextField label="Street" />
  <PlTextField label="City" />
</PlFieldset>;
dart
import 'package:plass_ui/plass_ui.dart';

PlFieldset(
  legend: const Text('Billing address'),
  description: const Text('Where the invoice goes.'),
  children: <Widget>[streetField, cityField],
);

Props

PropTypeDefaultDescription
legendReactNodeWhat the group is called. It becomes part of the accessible name of every control inside, so it has to read correctly in front of each of them — "Billing address", not "Where should we send it?"
descriptionReactNodeA line under the legend
disabledbooleanfalseDisables every control inside at once, the way a <fieldset> always has
sizeshared'xs' | 'sm' | 'md' | 'lg' | 'xl''md'The type scale of the legend and the gap between the controls
childrenReactNodeThe controls that answer one question together
PropTypeDefaultDescription
children * List<Widget>The controls that answer one question together
legendWidget?What the group is called. It becomes part of the accessible name of every control inside, so it has to read correctly in front of each of them — "Billing address", not "Where should we send it?"
descriptionWidget?A line under the legend
disabledboolfalseTakes the pointer and the focus away from everything inside, and drains the group. There is no browser-style cascade here
sizesharedPlassSizePlassSize.mdThe type scale of the legend and the gap between the controls

Every native <fieldset> attribute passes straight through. color is excluded because a fieldset has no surface to colour.

What the shared axes mean across the library is in prop conventions.

The three things it owns

Three things, and nothing else:

  • The legend, which becomes part of the accessible name of every control inside. That is why it has to be a phrase that still reads correctly in front of each of them, "Billing address", not "Where should we send it?".
  • The gap the controls stand at, on the sheet ladder.
  • disabled, which is the one thing only a real <fieldset> can do: it reaches every control inside, including one a component three levels down rendered and never heard of.

It draws no surface and takes no color, variant or elevation. A group of fields is a grouping; put it inside a PlCard or a PlBox when a sheet is wanted.

Examples

disabled

The reason to use a fieldset rather than a <div>. Turning it on takes every control inside out of the tab order and out of the form, without the fieldset knowing what any of them are.

React

size

The legend's type scale and the gap between the controls, on the sheet ladder, the same one a PlCard scores its sections with, because a fieldset is a section of a form rather than a control in one.

React

Inside a sheet

Two fieldsets on one card is the usual arrangement, and it is what makes the no-surface rule pay: the card is the sheet, and each group is a name and a gap on it.

React

Two browser defaults undone

A <fieldset> arrives with a border, padding and a margin of its own, and none of the three is the library's. They are undone.

So is min-width: min-content, which every browser gives a fieldset and nothing else. It is the reason a fieldset holding a wide table refuses to shrink inside a flex row, and min-w-0 is what puts it back.

Differences from the React build

ReactFlutterWhy
disabled as the native <fieldset> attributethe pointer taken away, the focus taken away, the group drainedThere is no such cascade in Flutter. This does the three things the attribute actually buys; what it cannot do is make a field inside report itself as unavailable, so a field that has to say so is given its own disabled.
a <fieldset> whose browser border, padding, margin and min-width are undonea ColumnThere is nothing to undo.
the legend as part of every control's accessible namethe legend as a heading above a named containerFlutter has no <fieldset>/<legend> pairing to inherit, and prefixing every control's own name would say the group's name once per control.
childrenchildren: List<Widget>The stack is laid out here, so it counts what it is given.
className, style, native attributesThere is no class list and no style attribute to pass through.

Accessibility

  • It is a real <fieldset>, which is a group, and the legend names it.
  • The legend is a <div> pointed at by aria-labelledby rather than a rendered <legend>. That is Base UI's decision, and it is what makes the group an ordinary flex container: a real <legend> is lifted out of its fieldset's content box by every browser, so a gap would put no space under it at all.
  • disabled on the fieldset is the native attribute, so it disables descendants the way the platform does, no context, no prop threading, and nothing to forget on a control that was added later.
  • A fieldset with neither legend nor description draws no heading block at all. An empty name is worse than none: it puts a blank in front of every control's own.

Released under the MIT License