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
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| Prop | Type | Default | Description |
|---|---|---|---|
| legend required | string | — | 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. |
| children required | content | — | The fields as direct children, usually Inputs, Checkboxes or Switches; Fieldset renders the Stack around them. |
| description | string | — | 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. |
| error | string | — | 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. |
| disabled | boolean | false | 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. |
| gap | tight | normal | loose | normal | Gap between the fields, from the layout rhythm. Fieldset renders the Stack itself; children are the raw fields. |
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
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.
This component does not animate, so it has no reduced-motion behaviour to describe.