Fieldset

Groups related fields under a legend so the relationship is announced, with an optional description and a group-level error — the container RadioGroup uses internally, exposed for any set of fields.

Basic usage

Install the package:

pnpm add @design-schema/react @design-schema/tokens

Import the stylesheet once for the app, then the component:

import '@design-schema/tokens/calm-precise/css';
import '@design-schema/react/index.css';
import { Fieldset } from '@design-schema/react';

Examples

Shipping address
import { Fieldset, Input } from '@design-schema/react';

const addressFields = [<Input key="street" label="Street" name="street" />, <Input key="city" label="City" name="city" />];

<Fieldset legend="Shipping address">{addressFields}</Fieldset>

Props

6 rows
PropTypeDefaultDescriptionPlatforms
legend requiredstring—

The group's name — what the fields together describe ("Shipping address", "Notification preferences"). Always visible.

Accessibility: The accessible name of the group; screen readers read it before each field inside.

web, lit, rn, swiftui
children requiredcontent—

The fields as direct children, usually Inputs, Checkboxes or Switches; Fieldset renders the Stack around them.

web, lit, rn, swiftui
descriptionstring—

Persistent helper text under the legend. An empty string counts as unset (no part rendered, no link, no accessibilityHint).

Accessibility: Linked with aria-describedby on the group.

web, lit, rn, swiftui
errorstring—

A group-level error (cross-field validation such as "End date must be after start date"). Field-level errors stay on the fields. An empty string counts as unset (no part, no aria-invalid, no announcement).

Accessibility: Rendered once under the group, only while set (no empty live region; inserting the role=alert region announces it), and linked with aria-describedby; while set, the group carries aria-invalid="true" (accessibilityState invalid is not available on native, so the error text alone identifies it there). Appearing is what announces: a change from one error string to another re-renders the text inside the region that is already there and does not announce again.

web, lit, rn, swiftui
disabledbooleanfalse

Disables every field inside. The group wins in one direction only: a field may disable itself while the group is enabled, but it cannot opt out of a disabled group — `disabled={false}` on a child of a disabled Fieldset is overridden, and clearing the group never enables a field that was disabled on its own.

web, lit, rn, swiftui
gaptight | normal | loosenormal

Gap between the fields, from the layout rhythm. Fieldset renders the Stack itself; children are the raw fields.

web, lit, rn, swiftui

Accessibility

Decisions this component leaves to you

Supply an accessible name. This component has no text of its own to fall back on, so without one a screen reader announces its role and nothing else.

Supply the label. It is associated with the control for you, but the words are yours — a placeholder is not a label.

Supply the error text when the value is invalid. The component links and announces it; it cannot write it.

Required props that carry accessibility meaning: legend. Each is described in the props table above.

Implicit ARIA role: group.

Anatomy

grouplegenddescriptionfieldserrorMessage

What generation guarantees

Accessible name — Every instance carries a programmatic name. Where the component has no text of its own to use, the name is a prop the consumer supplies.

Label association — The label is programmatically associated with the control, not merely placed beside it.

Error identification — An invalid value is identified in text and linked to the control — never signalled by colour alone.

Contrast — Text and meaningful non-text contrast meets WCAG AA; the pairs are listed below.

Contrast pairs

3 rows
ForegroundBackgroundLevel
color.foregroundcolor.backgroundAA
color.foreground.mutedcolor.backgroundAA
color.foreground.dangercolor.backgroundAA

This component does not animate, so it has no reduced-motion behaviour to describe.